Reputation: 9427
I think flat_map is great when you have an array of hashes of variable lengths and you want to work with each one individually:
data = [
{'apple' => 'fruit'},
{'carrot' => 'vegetable'},
{'orange' => 'fruit', 'pear' => 'fruit'},
{'lettuce' => 'vegetable'}
]
data.flat_map(&:to_a).reduce([]) {|acc, (k,v)| acc << Hash[k,v] }
=> [{"apple"=>"fruit"}, {"carrot"=>"vegetable"}, {"orange"=>"fruit"}, {"pear"=>"fruit"}, {"lettuce"=>"vegetable"}]
But I don't think I fully understand flat_map. According to docs:
Returns a new array with the concatenated results of running block once for every element in enum.
But look at this example:
[[1], {a: "a"}, {b: "b"}].flat_map(&:to_a)
=> [1, [:a, "a"], [:b, "b"]]
The first item is stripped from the inner array, the other items are converted to arrays. How does it know to do this?
When I call to_a like this:
[1].to_a
=> [1]
[1].to_a.flatten
=> [1]
You see the result is different than what flat_map did. What is flat_map doing in the first example to strip the item of the array?
Upvotes: 3
Views: 4413
Reputation: 110685
For any array arr
having m+n
elements, if
arr.map { ... }
returns
[a1, a2,.., am, o1, o2,.., on]
where a1, a2,.., am
are arrays and o1, o2,.., on
are objects other than arrays, then by changing map
to flat_map
, leaving the block unchanged, flat_map
will return
[*a1, *a2,.., *am, o1, o2,.., on]
Example 1
For
arr = [[1], {a: "a"}, {b: "b"}]
we have
arr.map(&:to_a)
#=> [[1], [[:a, "a"]], [[:b, "b"]]]
so it follows that
arr.flat_map(&:to_a)
will return
[*[1], *[[:a, "a"]], *[[:b, "b"]]]
#=> [1, [:a, "a"], [:b, "b"]]
and indeed it does.
Example 2
Suppose now that we have
arr = [1, 'cat', [2,3], {a: 4}]
then
arr.map(&:itself)
#=> [1, "cat", [2, 3], {:a=>4}]
so
arr.flat_map(&:itself)
is found to return
[1, "cat", 2, 3, {:a=>4}]
which is the reduction of
[1, "cat", *[2, 3], {:a=>4}]
Upvotes: 3
Reputation: 198344
[1].flatten
is [1]
- there is no second level. [[1]].flatten
is [1]
. This is the difference between your last example and all others.
If you try plain map
first,
[[1], {a: "a"}, {b: "b"}].map(&:to_a)
# => [[1], [[:a, "a"]], [[:b, "b"]]]
Notice that each hash arrayifies to an array of arrays (each of which is a pair of a key and a value). When you flatten
it by one level:
[[1], {a: "a"}, {b: "b"}].map(&:to_a).flatten(1)
# => [1, [:a, "a"], [:b, "b"]]
This is exactly what you are getting with flat_map
.
Upvotes: 1