Converting Ruby array into a hash - arrays
I am attempting to write a method named my_transform that takes an array as follows:
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
and displays the items' indexes as follows:
item_to_position = {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
I should be able to execute:
my_transform(items) == item_to_position
and receive true.
I have contemplated using each_with_index. Should I begin by saying:
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
hash = Hash[*array]
def my_transform
I have to convert the string to a hash. Any help is appreciated.
I would use Array#to_h:
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
items.each_with_index.to_h
#=> { "Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4 }
Note that to_h was introduced in Ruby 2.1
Using to_h your my_transform method could look like this:
def my_transform(items)
items.each_with_index.to_h
end
You can do this in various ways.
Create an array and convert it to a hash
Until fairly recently, you would use the public class method Hash::[] to convert an array to a hash. It works like this:
h = Hash[ [[:a, 1], [:b, 2]] ]
#=> {:a=>1, :b=>2}
or
h = Hash[:a, 1, :b, 2]
#=> {:a=>1, :b=>2}
In Ruby v2.1.0 the methods Array#to_h and Enumerable#to_h were introduced. The first works like this:
h = [[:a, 1], [:b, 2]].to_h
#=> {:a=>1, :b=>2}
Therefore, to use Hash or to_h you must first create the array:
arr1 = [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
or
arr2 = ["Aqua", 0, "Blue", 1, "Green", 2, "Red", 3, "Yellow", 4]
In the second case we'd use it like this:
Hash[*arr2]
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
Let's first create arr1. You are right that you need to use Enumerable#each_with_index. You then need to use Enumerable#to_a to convert each element of items to an array [<color>, index].
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
arr = items.each_with_index.to_a
#=> [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
Let's look at this more closely:
enum = items.each_with_index
#=> #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]:each_with_index>
enum, an enumerator, is an instance of the class Enumerator. The Enumerator class is one of many classes that includes the Enumerable module, of which to_a is an instance method. Not only does:
arr = enum.to_a
#=> [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
convert the enumerator to the desired array, but it is a convenient way to view the elements of any enumerator (which are generally passed to either a block or to another enumerator).
So we can now create the hash:
h = Hash[arr]
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
or
h = Hash[*arr.flatten]
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
or
h = arr.to_h
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
Suppose now that we had:
items = ["Aqua", "Blue", "Green", "Aqua", "Aqua"]
We then obtain:
items.each_with_index.to_a.to_h
#=> {"Aqua"=>4, "Blue"=>1, "Green"=>2}
In building the hash, Ruby first creates the key-value pair "Aqua"=>0, which she later overwrites with "Aqua"=>3 and then with "Aqua"=>4. This is a consequence of the fact that hashes have unique keys.
Build the hash from scratch
Now suppose we start with an empty hash:
h = {}
(same as h = Hash.new) and add key-value pairs:
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
items.each_index { |i| h[items[i]] = i }
#=> ["Aqua", "Blue", "Green", "Red", "Yellow"]
h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
We could alternatively write:
items.size.times { |i| h[items[i]] = i }
#=> 5
h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
or
(0...items.size).each { |i| h[items[i]] = i }
#=> 0...5
h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
The Ruby way is skip the step h = {} and to use each_with_index, as before, together with Enumerator#with_object:
items.each_with_index.with_object({}) { |(s,i),h| h[s] = i }
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
The "object" in with_object is a hash, with_object's argument being its initial value, here an empty hash. This object is represented by the block variable h and is returned after all elements of items have been enumerated (so we don't need a subsequent line h to return the hash).
Lets look at the steps that are performed here. First, we have
enum0 = items.each_with_index
#=> #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]:each_with_index>
which I discussed earlier. Then Ruby computes
enum1 = enum0.with_object({})
#=> #<Enumerator: #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]
:each_with_index>:with_object({})>
Examine the return value carefully. As you see, enum1, like enum0, is an enumerator. You might think of it as a "compound enumerator". To see the values of enum1 that will be passed to the block, you can convert it to an array:
enum1.to_a
#=> [[["Aqua", 0], {}], [["Blue", 1], {}], [["Green", 2], {}],
# [["Red", 3], {}], [["Yellow", 4], {}]]
As you see, enum1 has five elements, each an array containing an array and a hash. The elements of enum1 are passed to the block by Enumerator#each, (which calls Array#each):
enum1.each { |(s,i),h| h[s] = i }
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
We can use Enumerator#next to pass each element of enum1 to the block, and set the block variables to its value. The first is:
(s,i),h = enum1.next
#=> [["Aqua", 0], {}]
s #=> "Aqua"
i #=> 0
h #=> {}
Notice how [["Aqua", 0], {}] is decomposed into its three constituent elements and each block variable is set equal to one of the elements. This is called array decomposition.
We can now perform the block calculation:
h[s] = i
#=> {}["Aqua"] = 0
so now:
h #=> {"Aqua"=>0}
Then the second element is passed to the block:
(s,i),h = enum1.next
#=> [["Blue", 1], {"Aqua"=>0}]
s #=> "Blue"
i #=> 1
h #=> {"Aqua"=>0}
Notice how h has been updated. The block calculation is now:
h[s] = i
#=> {"Aqua"=>0}["Blue"] = 1
and now:
h #=> {"Aqua"=>0, "Blue"=>1}
The remaining calculations are performed similarly. After all elements of enum1 have been enumerated, enum1.each returns h.
def my_transform(arr)
arr.inject({}) {|m,e| m[e] = arr.index(e); m }
end
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
def my_transform(items)
Hash[items.each_with_index.map { |value, index| [value, index] }]
end
Most Rubies
This works at least as far back as Ruby 1.9.3.
# Verbose, but flexible!
def hasherize *array
hash = {}
array.flatten!
array.each_with_index { |key, value| hash[key] = value }
hash
end
# Pass a single array as an argument.
hasherize %w(Aqua Blue Green Red Yellow)
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
# Pass multiple arguments to the method.
hasherize :foo, :bar, :baz
#=> {:foo=>0, :bar=>1, :baz=>2}
Ruby >= 2.1.0
If you're running a recent Ruby, you can simplify the above to:
def hasherize *array
array.flatten.each_with_index.to_h
end
The results will be the same as above, but the Array#to_h method simplifies the code a lot. However, you still need to flatten the array to avoid results like:
#=> {["Aqua", "Blue", "Green", "Red", "Yellow"]=>0}
You can also try this.
e.g.
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
items.inject({}) do |tmphash, (k,v)|
tmphash[k] = items.index(k)
tmphash
end
## OUTPUT
{"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
Related
Ruby: Use Nested Arrays to Compile Values with Similar First Item
I have a Ruby nested array like this: arr = [["Red", 2], ["Red", 1], ["Yellow", 0], ["Yellow", 2], ["Blue", 1]] And I'm trying to find a method that adds up the number (which is the second item in each mini-array) for similar items, yielding a nested array like this: [["Red", 3], ["Yellow", 2], ["Blue", 1]] I've tried for loops, and if statements, but I can't figure out exactly how to do it. With a non-nested array you could just use if and .includes?, but I'm not sure how to work that with a nested array. Any Ruby wizards able to steer me in the right direction here?
Note that this is not an array nor valid ruby ["Red", 3], ["Yellow", 2], ["Blue", 1] anyway try this arr.group_by { |color, amount| color } .map { |k, color_arr| [k, color_arr.map(&:last).inject(:+)] } => [["Red", 3], ["Yellow", 2], ["Blue", 1]] or from ruby 2.4.0 and upwards arr.group_by { |color, amount| color } .map { |k, color_arr| [k, color_arr.map(&:last).sum] }
arr.each_with_object(Hash.new(0)) {|(col,nbr),h| h[col] += nbr}.to_a #=> [["Red", 3], ["Yellow", 2], ["Blue", 1]] Note the intermediate calculation: arr.each_with_object(Hash.new(0)) {|(col,nbr),h| h[col] += nbr} #=> {"Red"=>3, "Yellow"=>2, "Blue"=>1} See Hash::new.
How do I can use functional programing to transform dictionary to IndexPath array?
I have this kind of dictionary. let sections = ["A": ["Item1", "Item2", "Item3", "Item4"], "B": ["Item1", "Item2", "Item3", "Item4"]] I need to fetch all keys and sort them before IndexPath array creation. let sectionsKeys = Array(sections.keys).sorted() Now I need to transform this dictionary to IndexPath array. var indexPathes = [[0, 0], [0, 1], [0, 2], [0, 3], [1, 0], [1, 1], [1, 2], [1, 3]] So my attempt is quite straightforward. for (sectionPos,key) in sectionsKeys.enumerated() { guard let items = sections[key] as? [String] else { break } for (itemPos,_) in items.enumerated() { let indexPath = IndexPath(row: itemPos, section: sectionPos) indexPathes.append(indexPath) } } In this way I receive expected result. But I am looking for solution that will use functional programing to achieve same result.
First of all, you should sort the sections Dictionary (resulting in an array of tuples), since Dictionary is unordered by definition, so there's no guarantee that by iterating through the Dictionary itself you will get the desired results. Afterwards you can enumerate the ordered collection, then use a combination of flatMap and map to create the IndexPaths. You need flatMap when iterating through the sections to flatten out the array of IndexPaths returned by the nested map. Instead of iterating through an enumerated collection to only get the indexes, you can just iterate through the indices themselves using section.value.indices, where value is of type [String]. let sections = ["Section 1": ["Item1", "Item2", "Item3", "Item4"], "Section 2": ["Item1", "Item2", "Item3", "Item4"]] let indexPaths = sections.sorted(by: {$0.key < $1.key}).enumerated().flatMap({sectionPos, section->[IndexPath] in return section.value.indices.map({ itemPos->IndexPath in IndexPath(row: itemPos, section: sectionPos) }) }) Output: [[0, 0], [0, 1], [0, 2], [0, 3], [1, 0], [1, 1], [1, 2], [1, 3]]
Ruby: multidimensional array to one-dimensional rows
In Ruby, how do I go from this: [ 1, ["green", "blue", "black"], [ ["1", "2"], ["3"], ["4", "5"] ] ] to this? [ [1, "green", "1"], [1, "green", "2"], [1, "blue", "3"], [1, "black", "4"], [1, "black", "5"], ] I tried .zip but with no luck. Any help is greatly appreciated, and of course I'm looking for a performant solution.
The pattern is not completely clear to me, but this gets the expected output for the example you provided: data[1]. zip(data[2]). flat_map { |x, ys| [x].product(ys) }. map { |zs| [data[0], *zs] } #=> [[1, "green", "1"], [1, "green", "2"], [1, "blue", "3"], # [1, "black", "4"], [1, "black", "5"]]
We are given arr = [1, ["green", "blue", "black"], [ ["1", "2"], ["3"], ["4", "5"] ]] Here are a couple of ways to obtain the desired result. #1 arr[1].flat_map.with_index { |color,i| [arr[0]].product([color], arr[2][i]) } #=> [[1, "green", "1"], [1, "green", "2"], [1, "blue", "3"], # [1, "black", "4"], [1, "black", "5"]] The steps are as follows. enum0 = arr[1].flat_map #=> #<Enumerator: ["green", "blue", "black"]:flat_map> enum1 = enum0.with_index #=> #<Enumerator: #<Enumerator: ["green", "blue", "black"]:flat_map>:with_index> enum1 can be thought of as a compound enumerator. We can see the values that will be generated by enum1 and passed to the block by converting it to an array. enum1.to_a #=> [["green", 0], ["blue", 1], ["black", 2]] The first value is generated and passed to the block, the block variables are assigned and the block calculation is performed. color, i = enum1.next #=> ["green", 0] color #=> "green" i #=> 0 [arr[0]].product([color], arr[2][i]) #=> [1].product(["green"], ) #=> [[1, "green", "1"], [1, "green", "2"]] The calculations are similar for the two remaining elements generated by enum1. An alternative to this is to dup arr[2] and shift elements of the dup: a2 = arr[2].dup arr[1].flat_map { |color,i| [arr[0]].product([color], a2.shift) } #2 arr[1].zip(arr[2]).flat_map { |color, a| [arr[0]].product([color], a) } #=> [[1, "green", "1"], [1, "green", "2"], [1, "blue", "3"], # [1, "black", "4"], [1, "black", "5"]] The steps are as follows. b = arr[1].zip(arr[2]) #=> [["green", ["1", "2"]], ["blue", ["3"]], ["black", ["4", "5"]]] b[0] is passed to flat_map and the block variables are assigned and the block calculation is performed. color, a = b[0] #=> ["green", ["1", "2"]] color #=> "green" a #=> ["1", "2"] [arr[0]].product([color], a) #=> [["1"]].product(["green"], ["1", "2"]) #=> [[1, "green", "1"], [1, "green", "2"]] After the remaining elements of b are passed to map the desired array is returned by Enumerable#flat_map.
I needed a more generic and flexible solution as compared to the ones proposed (my bad, I should have been more clear about the requirements), so I came up with the following: class Array def transpose_rows arys = self.select{|el| el.is_a?(Array)} if arys.size == 0 [self] else result = [] (arys.map(&:size).max || 1).times.map{ |i| self.map { |r| r.is_a?(Array) ? r[i] : r }.transpose_rows.map{|r| result << r} } result end end end The initial spec is that every element in the array is either a value or another array of varying depth. Each subarray "explodes" the values of the subarrays of depth-1 into an arbitrary number of "sub-values". The result should be a set of rows listing all combinations deriving from the original array. The other solutions proposed do work for the original array I posted, which was just an example, while this one works for more complex scenarios such as the following: [ 2, [3,4,5], 6, [7,8,9], [ [11,22], [33], [44,55] ], [0, 1, 2], [ [66], [77], [nil,99] ], 4 ].transpose_rows # => [ # [2, 3, 6, 7, 11, 0, 66, 4], # [2, 3, 6, 7, 22, 0, nil, 4], # [2, 4, 6, 8, 33, 1, 77, 4], # [2, 5, 6, 9, 44, 2, nil, 4], # [2, 5, 6, 9, 55, 2, 99, 4] # ]
How do I eliminate elements from my array if all their elements are empty?
I'm using Ruby 2.4. I have an array of arrays, which looks roughly like this [[2, 3, 4], ["", "", ""], ["a", "b", nil], [nil, "", nil], [151, "", "abcdef"]] How would I eliminate all the arrays in the above list if all of their elements are either nil or empty? After applying this function to the above, I'd expect teh result to be [[2, 3, 4], ["a", "b", nil], [151, "", "abcdef"]]
Something like this using reject and all: arr.reject { |ar| ar.all? { |e| e.to_s.empty? } } #=> [[2, 3, 4], ["a", "b", nil], [151, "", "abcdef"]] The key here is nil.to_s.empty? #=> true.
Something like this: array.select { |sub_array| sub_array.any? { |element| element.present? } } Or even shorter: array.select { |sub_array| sub_array.any?(&:present?) }
Adding an element to every item of an array in ruby
I currently have an input [['a', [0, 1]], ['b', [1]]]. I'm trying to combine the first item to every element in [0,1] i.e.: 'a' in ['a',[0,1] => [['a',0],['a',1],['b',1]] like ordered pairs. I've done it but it seems overly complicated I thought there might be a method I've overlooked. [[0, [0, 1]], [1, [1]]].map.with_index{|x,y| x[1].map{|ele| [y,ele]}}.flatten(1) #I used 'a'&'b' in the example to help with any confusion.
▶ arr.flat_map { |e| [e.first].product(e.last) } #⇒ [["a", 0], ["a", 1], ["b", 1]]
arr = [['a', [0, 1]], ['b', [1]]] arr.each_with_object([]) { |(x,a),b| a.each { |y| b << [x,y] } } #=> [["a", 0], ["a", 1], ["b", 1]]