Ruby: Use Nested Arrays to Compile Values with Similar First Item - arrays

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.

Related

Why is my enumeration stopping after first rejection in Ruby?

Desperate need of help. I am trying to remove arrays from and array of arrays, and I have hit a road block. Essentially, if the first value in the child-array doesn't exist in either position of any other child-arrays, then it should be deleted. (presume that the array will be sorted - cause it will be)
arr = [[0, 1], [2, 3], [4, 5]]
arr.each_with_index do |inside_array, index|
if !index.zero?
# arr.delete(arr[index]) if arr.select {|x| x.include?(inside_array[0])}.count < 2
# refactored
arr.reject! {|x| x.include?(inside_array[0])}
end
end
=> [[0, 1], [4, 5]]
# Why does it stop itterating/enumerating after the first deletion?
# Goal output is [[0, 1]] for this example
Similarly, an array such as [[0, 1], [2, 3], [1, 5]], should yield [[0, 1], [1, 5]]
-or -
[[0, 1], [2, 3], [0, 3]], should yield [[0, 1], [0, 3]]
You've tried to modify origin array. That's your problem.
In that cases you need to duplicate it like this:
arr = [[0, 1], [2, 3], [4, 5]]
arr.dup.each_with_index do |inside_array, index|
if !index.zero?
arr.reject! {|x| x.include?(inside_array[0])}
end
end
arr #=> [[0, 1]]
So just use dup
As for the second question (implementation of subarray removal), I suggest this refactoring:
def remove_subarray(arr)
arr.reject { |inside_array| (inside_array & arr.first).empty? }
end
remove_subarray([[0, 1], [2, 3], [4, 5]]) #=> [[0, 1]]
remove_subarray([[0, 1], [2, 3], [1, 5]]) #=> [[0, 1], [1, 5]]
remove_subarray([[0, 1], [2, 3], [0, 3]]) #=> [[0, 1], [0, 3]]

Condensing multidimensional array of permutations into groups

I have a multidimensional array in Ruby which looks like this:
[[1,12], [1,5], [1,6], [5,12], [6,12], [12,5], [12,6]]
I need to combine it into a new multidimensional array, grouping values together, to form valid combinations of (n) positions (n being the size of each element in the original array):
[
[[1,5,6], [12]], # [1,5,6].product([12]) #=> [[1, 12], [5, 12], [6, 12]]
[[1,12], [5,6]] # [1,12].product([5,6]) #=> [[1, 5], [1, 6], [12, 5], [12, 6]]
]
The purpose of this is to take an array of n-place permutations, and generate the smallest possible multidimensional array that lists the valid numbers in each place (while not including combinations that aren't present in the original array).
How can a multidimensional array containing n-place permutations be reduced down to the array described above? The built-in (and brilliant) array methods in Ruby don't seem to include a function for this, and short of generating every possible combination and then testing them against the original permutations, I'm not sure how to get it right.
I'm not quite sure if I correctly understand the problem, but given this array:
a = [[1, 12], [1, 5], [1, 6], [5, 12], [6, 12], [12, 5], [12, 6]]
I think you can group the left-hand items by the right-hand items:
h1 = Hash.new { |h, k| h[k] = [] }
a.each { |k, v| h1[v] << k }
h1
#=> {12=>[1, 5, 6], 5=>[1, 12], 6=>[1, 12]}
And apply the same transformation again:
h2 = Hash.new { |h, k| h[k] = [] }
h1.each { |k, v| h2[v] << k }
h2
#=> {[1, 5, 6]=>[12], [1, 12]=>[5, 6]}
This gives:
h2.to_a
#=> [
# [[1, 5, 6], [12]],
# [[1, 12], [5, 6]]
# ]
In recent versions of Ruby you could write it as:
a.group_by(&:last).transform_values { |v| v.map(&:first) }
.group_by(&:last).transform_values { |v| v.map(&:first) }
.to_a
#=> [
# [[1, 5, 6], [12]],
# [[1, 12], [5, 6]]
# ]
The condensed array can be expanded via:
[[[1, 5, 6], [12]], [[1, 12], [5, 6]]].flat_map { |a, b| a.product(b) }
#=> [[1, 12], [5, 12], [6, 12], [1, 5], [1, 6], [12, 5], [12, 6]]
Note that this attempt only works for sub-arrays with two elements, but it should get you started.

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]]

Converting Ruby array into a hash

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}

How to declare an empty 2-dimensional array in Ruby?

Can somebody please tell me how to declare a new instance of a 2-dimensional array?
Most of the languages use something like:
array = Array.new[2][2]
I don't know how to do it in Ruby.
You can do:
width = 2
height = 3
Array.new(height){Array.new(width)} #=> [[nil, nil], [nil, nil], [nil, nil]]
To declare 2d array in ruby, Use following syntax with initialization value
row, col, default_value = 5, 4, 0
arr_2d = Array.new(row){Array.new(col,default_value)}
=> [[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
We can do any level of nesting, like for 3d array(5 x 4 x 2): you can pass block to initialize array in most inner Array
z = 2
arr_3d = Array.new(row){Array.new(col){Array.new(z){|index| index}}}
=> [[[0, 1], [0, 1], [0, 1], [0, 1]],
[[0, 1], [0, 1], [0, 1], [0, 1]],
[[0, 1], [0, 1], [0, 1], [0, 1]],
[[0, 1], [0, 1], [0, 1], [0, 1]],
[[0, 1], [0, 1], [0, 1], [0, 1]]]
Now, you can access its element using [] operator like arr_2d[0][1], actually its array of arrays
You could also initialize passing a value:
Array.new(3) { Array.new(3) { '0' } }
Output:
[
["0", "0", "0"],
["0", "0", "0"],
["0", "0", "0"]
]
You can declare a multidimensional array in Ruby with:
Array.new(Number_of_ROWs){Array.new(Number_of_COLUMNs)}
How To Use This Syntax
Let us understand it by using above example i.e. array = Array.new[2][2].
So, in this example we've to declare an empty multidimensional array with 2 rows and 2 column.
Let us start implementing the our syntax now,
array = Array.new(2){Array.new(2)}
Now you've an array with 2 rows and 2 column with nil values.
Now the array variable contains [[nil, nil], [nil, nil]] which is consider as an empty multidimensional array or nil value multidimensional array.
simply:
array = Array.new(8,Array.new(8))

Resources