Related
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.
If I have an array:
%w(a b c d e)
=> ["a","b","c","d","e"]
I can get some combinations with
irb(main):071:0> %w(a b c d e).combination(3).to_a
=> [["a", "b", "c"], ["a", "b", "d"], ["a", "b", "e"], ["a", "c", "d"], ["a", "c", "e"], ["a", "d", "e"], ["b", "c", "d"], ["b", "c", "e"], ["b", "d", "e"], ["c", "d", "e"]]
However that is not ALL the combinations, just the unique ones, e.g. ["e", "a", "b"] is missing
When I similarly try with s smaller array I only get one result:
irb(main):059:0> %w(a b c).combination(3).to_a
=> [["a", "b", "c"]]
How can I get all 6 combinations, i.e. for ['a', 'b', 'c'] I want to get
[['a','b','c'], ['a','c','b'], ['b', 'a', 'c'], ['b', 'c', 'a'], ['c', 'a', 'b'], ['c', 'b', 'a']
Similarly for [1,2,3,4] if I want all the 3 digit combo I should get
irb(main):074:0> [[1,2,3],[1,2,4],[1,3,2],[1,3,4],[1,4,2],[1,4,3], [2,1,3],[2,1,4],[2,3,4],[2,3,1],[2,4,1],[2,4,2], [3,1,2],[3,1,4],[3,2,3],[3,2,4],[3,4,2],[3,4,1]]
?
You are looking for permutation instead of combination.
In combinations, we do not care about the order of the elements, and only care about the presence of all the elements in the set.
[1,2,3,4].permutation(3).to_a
#=> [[1, 2, 3], [1, 2, 4], [1, 3, 2], [1, 3, 4], [1, 4, 2], [1, 4, 3], [2, 1, 3], [2, 1, 4], [2, 3, 1], [2, 3, 4], [2, 4, 1], [2, 4, 3], [3, 1, 2], [3, 1, 4], [3, 2, 1], [3, 2, 4], [3, 4, 1], [3, 4, 2], [4, 1, 2], [4, 1, 3], [4, 2, 1], [4, 2, 3], [4, 3, 1], [4, 3, 2]]
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]]
Given:
a = [[1,"a"],[2,"b"]]
b = [[3,"c"],[4,"d"]]
I want to turn a into [[1,"a"],[2,"b"][3,"c"],[4,"d"]]. How can do this without +? It creates a new array, which I want to avoid.
(a << b).flatten(1)
# => [1, "a", 2, "b", [3, "c"], [4, "d"]]
a.concat(b)
...............................
> b.inject(a, :<<)
#=> [[1, "a"], [2, "b"], [3, "c"], [4, "d"]]
a = [[1,"a"],[2,"b"]]
b = [[3,"c"],[4,"d"]]
a[a.length, 0] = b
a
# > [[1, "a"], [2, "b"], [3, "c"], [4, "d"]]
concat is the answer, but you could do this:
a.object_id #=> 70223889895340
a.replace(a+b) #=> [[1, "a"], [2, "b"], [3, "c"], [4, "d"]]
a #=> [[1, "a"], [2, "b"], [3, "c"], [4, "d"]]
a.object_id #=> 70223889895340
What about?
a.push(b.shift) while b.any?
How about this?
a + b
=> [[1, "a"], [2, "b"], [3, "c"], [4, "d"]]
Is there an elegant way to turn a nested array of the form
[["a", 1], ["a", 2], [nil, 3], [nil, 4], ["b", 6], ["b", 8]]
into a hash of the form
{"a" => [1,2], nil => [3,4], "b" => [6,8]}
This is one way:
arr = [["a", 1], ["a", 2], [nil, 3], [nil, 4], ["b", 6], ["b", 8]]
h = Hash.new {|hash, key| hash[key] = []}
arr.each {|e| h[e[0]] << e[1]}
p h #=> {"a"=>[1, 2], nil=>[3, 4], "b"=>[6, 8]}
ary = [['a', 1], ['a', 2], [nil, 3], [nil, 4], ['b', 6], ['b', 8]]
ary.group_by(&:first).
# => { 'a' => [['a', 1], ['a', 2]], nil => [[nil, 3], [nil, 4]], 'b' => [['b', 6], ['b', 8]] }
map {|k, v| [k, v.map(&:last)] }.
# => [['a', [1, 2]], [nil, [3, 4]], ['b', [6, 8]]]
to_h
# => { 'a' => [1, 2], nil => [3, 4], 'b' => [6, 8] }
One way could be:
array = [['a', 1], ['a', 2], [nil, 3], [nil, 4], ['b', 6], ['b', 8]]
array.each_with_object(Hash.new{|h,k| h[k] = []}) {|a, obj| obj[a.first] << a.last }
# => {"a"=>[1, 2], nil=>[3, 4], "b"=>[6, 8]}
array.each_with_object({}){|a, h| (h[a.first]||=[] )<< a.last }
ary = [['a', 1], ['a', 2], [nil, 3], [nil, 4], ['b', 6], ['b', 8]]
ary.group_by(&:first).map {|k, v| {k => v.map(&:last)} }