Get subset from list of matching ids

Array1 contains a list of strings. Array_set contains a list of keyword lists.

array1 = ["1","1","3","2","4"]
array_set = [
[key1: "string", key2: "another string", key3: 0, id: 1],
[key1: "string", key2: "another string", key3: 0, id: 2],
[key1: "string", key2: "another string", key3: 0, id: 3],
[key1: "string", key2: "another string", key3: 0, id: 4],
[key1: "string", key2: "another string", key3: 0, id: 5],
[key1: "string", key2: "another string", key3: 0, id: 6],
[key1: "string", key2: "another string", key3: 0, id: 7]
]

I’m trying to get the following output by matching the string values of array1 with the id in array_set. Duplicates are allowed in array1 and output should match the original order of array1.

Example of what I’m trying to achieve. Looking for some help on this. Thank you!!

output = [
[key1: "string", key2: "another string", key3: 0, id: 1],
[key1: "string", key2: "another string", key3: 0, id: 1],
[key1: "string", key2: "another string", key3: 0, id: 3],
[key1: "string", key2: "another string", key3: 0, id: 2],
[key1: "string", key2: "another string", key3: 0, id: 4]
]
array1 = ["1", "1", "3", "2", "4"]

array_set = [
  [key1: "string", key2: "another string", key3: 0, id: 1],
  [key1: "string", key2: "another string", key3: 0, id: 2],
  [key1: "string", key2: "another string", key3: 0, id: 3],
  [key1: "string", key2: "another string", key3: 0, id: 4],
  [key1: "string", key2: "another string", key3: 0, id: 5],
  [key1: "string", key2: "another string", key3: 0, id: 6],
  [key1: "string", key2: "another string", key3: 0, id: 7]
]

array1
|> Enum.map(&String.to_integer/1)
|> Enum.map(fn id ->
  Enum.find(array_set, nil, fn item -> Keyword.get(item, :id) == id end)
end)
|> IO.inspect()

One important concept of writing Elixir is transforming the data little by little.

2 Likes

Thank you! That helped a lot as I didn’t originally consider using Enum.find

Depending on the site of the array_set it might help to transform the list into a map by :id first, eg by using Enum.group_by to get a quicker lookup.

4 Likes

Does array_set allow duplicate ids? If it’s possible for 2 members of the set to have the same id: field, you could use Enum.filter/2:

defmodule FilterSet do
  def match_ids(list, set) do
    list = Enum.map(list, &String.to_integer/1)
    Enum.filter(set, fn member -> Keyword.get(member, :id) in list end)
  end
end
2 Likes

If the elements of array_set have unique IDs, you could transform them into a map and look up the elements of array1:

array_set_as_map = Map.new(array_set, fn kw -> {Keyword.get(kw, :id), kw} end)

array1
|> Enum.map(&String.to_integer/1)
|> Enum.map(&array_set_as_map[&1])
2 Likes

Because I love for comprehensions …

for sid <- array1, 
    nid = String.to_integer(sid), 
    set <- array_set, 
    {:id, ^nid} <- set, 
  do: set
5 Likes

I’d argue that Enum.group_by is more expressive to create a map:

array_set_as_map = Enum.group_by(array_set, &Keyword.get(&1, :id))

array1
|> Enum.map(&String.to_integer/1)
|> Enum.map(&array_set_as_map[&1])
2 Likes

group_by can be handy if you’re grouping by something that isn’t unique, but here it puts an extra layer of wrapping around the resulting keyword lists:

[
  [[key1: "string", key2: "another string", key3: 0, id: 1]],
  [[key1: "string", key2: "another string", key3: 0, id: 1]],
  [[key1: "string", key2: "another string", key3: 0, id: 3]],
  [[key1: "string", key2: "another string", key3: 0, id: 2]],
  [[key1: "string", key2: "another string", key3: 0, id: 4]]
]

since the values returned by group_by are lists, not single elements.

Changing the last Enum.map to Enum.flat_map will handle that unwrapping, but since we KNOW that id values are unique from the problem statement, it seems wasteful to defensively program against duplicated values.

1 Like