Flat list of Map to nested Map

Hi,

Given this List of Map:

[
  %{
    "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.494014",
    "00080050" => "102701.63",
    "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
    "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
  },
  %{
    "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.45651",
    "00080050" => "102701.63",
    "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
    "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
  },
  %{
    "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.598229",
    "00080050" => "102701.63",
    "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
    "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491496"
  }
]

I would like to group by 0020000E, to get this:


[
  %{
    "00080050" => "102701.63",
    "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
    "SERIES" => [
        %{
          "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495",
          "INSTANCES" => [
            %{
              "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.494014",
            },
            %{
              "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.45651",
            }
          ]
        },
        %{
          "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491496",
          "INSTANCES" => [
            %{
              "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.598229",
            }
          ]
        }
    ]
  }
]

How would you approach this?

Are all the values of key "00080050" the same?
Are all the values of key "0020000D" the same?

To simplify, yes. In reality there are edge cases where 0020000D can be different for the same 00080050, but let’s assume they don’t change for now.

I would solve it myself first, and then post my solution asking for feedback, rather that posting asking for the solution. :slight_smile:

2 Likes

This gives, more or less what I need:

[
    %{
          "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.494014",
          "00080050" => "102701.63",
          "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
          "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
        },
    %{
          "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.45651",
          "00080050" => "102701.63",
          "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
          "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
        },
    %{
          "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.598229",
          "00080050" => "102701.63",
          "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
          "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491496"
        }
]|> Enum.group_by(&(Map.get(&1, "0020000E")))
 |> IO.inspect

The result is:

%{
  "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495" => [
    %{
      "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.494014",
      "00080050" => "102701.63",
      "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
      "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
    },
    %{
      "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.45651",
      "00080050" => "102701.63",
      "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
      "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
    }
  ],
  "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491496" => [
    %{
      "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.598229",
      "00080050" => "102701.63",
      "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
      "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491496"
    }
  ]
}

But the group must include, 00080050 and 0020000D also.

I think I got it:

[
    %{
          "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.494014",
          "00080050" => "102701.63",
          "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
          "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
        },
    %{
          "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.45651",
          "00080050" => "102701.63",
          "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
          "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
        },
    %{
          "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.598229",
          "00080050" => "102701.63",
          "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
          "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491496"
        }
]
|> Enum.group_by(&(Map.get(&1, "0020000E")))
|> Enum.map(fn {l, arr} ->
  %{"SERIES" => l, 
    "IMAGES" => arr}
  end)
|> IO.inspect()

Results in:

[
  %{
    "IMAGES" => [
      %{
        "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.494014",
        "00080050" => "102701.63",
        "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
        "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
      },
      %{
        "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.45651",
        "00080050" => "102701.63",
        "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
        "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
      }
    ],
    "SERIES" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
  },
  %{
    "IMAGES" => [
      %{
        "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.598229",
        "00080050" => "102701.63",
        "0020000D" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778",
        "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491496"
      }
    ],
    "SERIES" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491496"
  }
]

Now, the problem is, “00080050” and “0020000D” must be at the top level.

Maps don’t have guaranteed sorted order so maybe as a final step you can convert the result to a Keyword list and sort by your preferred keys first?

I think group_by + map is probably a good approach. But I would use that just to build up the SERIES list:

series =
  list
  |> Enum.group_by(& &1["0020000E"])
  |> Enum.map(fn {series, instances} ->
    %{
      "0020000E" => series,
      "INSTANCES" => Enum.map(instances, &Map.take(&1, ["00080018"]))
    }
  end)

That gives you this:

[
  %{
    "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495",
    "INSTANCES" => [
      %{
        "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.494014"
      },
      %{
        "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.45651"
      }
    ]
  },
  %{
    "0020000E" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491496",
    "INSTANCES" => [
      %{
        "00080018" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.598229"
      }
    ]
  }
]

Then take the first element of the original list, extract the 2 common values, and add the series to it:

list
|> hd()
|> Map.take(["00080050", "0020000D"])
|> Map.put("SERIES", series)

Which gives the same output as your desired output in the first post.

I finally got the desired result:

list
|> Enum.group_by(&{Map.get(&1, "0020000D"), Map.get(&1, "00080050")})
|> Enum.map(fn {l, arr} ->
  %{ 
    "StudyInstanceUID" => elem(l, 0),
    "AccessionNumber" => elem(l, 1),
    "Series" => 
      Enum.group_by(arr, &(Map.get(&1, "0020000E")))
        |> Enum.map(fn {l, arr} ->
          %{
            "SeriesInstanceUID" => l,
            "Images" => Enum.map(arr, fn el ->
              %{"SOPInstanceUID" => el["00080018"]}

            end)
          }
          end)
  }
  end)

Which gives this:

[                                                                                                                                                                                                             
  %{
    "AccessionNumber" => "102701.63",
    "Series" => [
      %{
        "Images" => [
          %{
            "SOPInstanceUID" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.494014"
          },
          %{
            "SOPInstanceUID" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.45651"
          }
        ],
        "SeriesInstanceUID" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491495"
      },
      %{
        "Images" => [
          %{
            "SOPInstanceUID" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108776.598229"
          }
        ],
        "SeriesInstanceUID" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108775.491496"
      }
    ],
    "StudyInstanceUID" => "1.2.392.200036.9116.2.6.1.16.1613463063.1702108371.89778"
  }
]

BTW, Elixir is awesome!.