List to map

Hey, everybody,

couldn’t find a way to get the map from the list.

[1,2,3,4] - source list

 %{{1, 2} => true, {3, 4} => true} - the map to which you want to convert
1 Like

Can you explain the algorithm you’re looking for here? You want each pair of values as a key?

Let’s start by breaking the problem down: how can you turn [1,2,3,4] in to pairs of numbers, like [[1,2], [3,4]]?

1 Like

The challenge is as follows
the database query returns a list to me
users_pair = [[1, 2], [3, 4]].

I transformed it.
users_pair = List.flatten(users_pair)

then I have to turn it into a map:
map = %{{1, 2} => true, {3, 4} => true}

it requires me to be able to determine if there is such a pair or not, using map[{1,2}].

A list is an ordered collection of values.

A map is a key/value data structure, you can’t naturally get a map from a list without providing a function which will transform your data.

Looking at your example

The keys would be a tuple, and the values would be the true

As @benwilson512 suggested, you could break the problem and start by trying to get the pairs of values, which will form the keys in the map.

Flattening the list actually moves you further from the solution. Check out https://hexdocs.pm/elixir/List.html#to_tuple/1 and https://hexdocs.pm/elixir/Map.html#new/2

2 Likes

Also Enum.zip/2 looks promising. (Actually even Stream.zip/2 if the length of the list is not known upfront.) Or just for k <- [[1, 2], [3, 4]], into: %{}, do: {k, true}.

Shouldn’t k be a tuple? In your case it’s a list?!

Is my understanding correct that you need to check if a pair is present in the list? If so, why do you need to transform it in the first place?

I want to get a map that’s cheaper than a search on the list.

If this is the goal, what about:

# make a MapSet out of the pairs
pairs = MapSet.new(users_pair)

# check if a specific pair is in the set:
MapSet.member?(pairs, [3, 4])

# or even:
[3, 4] in pairs

If you only have a small number of pairs, you can use [3, 4] in users_pair without even turning it into a set, but that will be inefficient if the list gets long.

One caveat: if [1, 2] and [2, 1] should be considered the same, you will have to make sure the pairs are always ordered in the same way. Otherwise, MapSet will consider them as different.

1 Like

tuple into the map causes an error…

users = [[1, 3], [1, 2], [2, 3], [2, 4], [4, 1]].
tupple = List.to_tuple(users) # [[1, 3], [1, 2], [2, 3], [2, 4], [4, 1]}.
Map.new(users, fn x -> {x, :true} end)

else

users = [[1, 3], [1, 2], [2, 3], [2, 4], [4, 1]].
Map.new(users, fn x -> {x, :true} end)

return
%{
  [1, 2] => true,
  [1, 3] => true,
  [2, 3] => true,
  [2, 4] => true,
  [4, 1] => true
}

Strictly speaking You could do it like this

iex> list = [1,2,3,4]
iex> for [x, y] <- Enum.chunk_every(list, 2), reduce: %{} do acc -> Map.put(acc, {x, y}, true) end
%{{1, 2} => true, {3, 4} => true}

But many people find it strange as a requirement.

It’s supposed to be a lot of steam. Sorting is done in a model

You are turning the whole list into a tuple. If you want tuples instead of lists for the pairs, you should turn each pair into a tuple instead. Elaborating on my previous answer, this would be the result:

# Make a MapSet of pairs
pairs = MapSet.new([[1, 3], [1, 2], [2, 3], [2, 4], [4, 1]], fn [a, b] -> {a, b} end)

# Check if one pair is in the set
MapSet.member?(pairs, {3, 4})

# Or, equivalently:
{3, 4} in pairs

No need to flatten, turn into a map, etc. if you just want to efficiently test for inclusion in a set. Just use an actual set instead :wink:

That said, if this data comes from the database, it’s probably better to write a database query that checks if the pair exists or not, rather than loading all of them and checking that in Elixir, which requires transferring all the pairs and storing them in memory.

2 Likes

Depending on the amount of keys you actually want to look up, it might be a lot cheaper to just do it in the list…

If you only check for 2 or 3 keys, converting the list to a set might even be more expensive than just looking items up in the list.

Not necessarily in terms of algorithmic complexity, but in wall clock time and or memory consumption.

By task, meetings between users are generated once a day, and they must not overlap again.

link to the post where the code is provided (Is there a way to load a complex index from the database into redis?)

I wanted to improve the code so that I wouldn’t make a request to the database every time to check for matches, because the more users there are, the more load it will have.

Thank you, everyone.

These two options work. It should be faster than every time to query the database to check for pairs).
Now I need to see what’s faster of these options and why.)

iex> list = [1,2,3,4]
iex> for [x, y] <- Enum.chunk_every(list, 2), reduce: %{} do acc -> Map.put(acc, {x, y}, true) end
%{{1, 2} => true, {3, 4} => true}
pairs = MapSet.new([[1, 3], [1, 2], [2, 3], [2, 4], [4, 1]], fn [a, b] -> {a, b} end)
MapSet.member?(pairs, {3, 4})