How to expand elements of an array into sub-arrays? - ruby-on-rails

I have a huge array
huge = 1000
huge_array = (1..huge).to_a
How to best "expand" this array so that each element becomes a sub-array of format [original_element, "default value"], preferably in a memory-friendly way (without an explicit #map loop?)
expanded_huge_array = huge_array.some_magic
#=> [[1, "default value"],[2, "default value"], ... [1000, "default value"]]

huge_array.zip(['default value'] * huge_array.size)
BTW, you might simulate this behaviour with Hash with default:
arr = Hash.new { |h, key| huge_array.include?(key) ? [key, 'default value'] : nil }
arr[1]
#⇒ [1, 'default value']
arr[10000]
#⇒ nil

Try Array#product:
Returns an array of all combinations of elements from all arrays.
>> [1,2,3].product(["a"])
=> [[1, "a"], [2, "a"], [3, "a"]]

Related

Efficient way to subtract arrays and get index of resulting subarray

Lets say I have the following arrays:
arr1 = [
['a', 'b'],
['c', 'd'],
['e', 'f']
]
arr2 = [
['g', 'h'],
['i', 'k'],
['a', 'b']
]
I want to find the elements in arr1 that do not exist in arr2 and the index of the elements in arr1
I can do this with the following but this isn't very efficient and is not a pretty solution. There can also be many elements in the arrays so this does not scale. Is there a better way to do this?
diff = arr1 - arr2
diff_with_index = diff.map { |x| { index: arr1.index(x), values: x } }
print diff_with_index
# [{:index=>1, :values=>["c", "d"]}, {:index=>2, :values=>["e", "f"]}]
When you have to do multiple include? checks, the most efficient way is to turn one of the lists into a set or hash beforehand, so you can have O(1) lookup time, so something like this:
require 'set'
arr2_set = Set.new(arr2)
arr1.each_index.select { |idx| !arr2_set.include?(arr1[idx]) }
Here is one way.
i1 = (0..arr1.size-1).to_a
#=> [0, 1, 2]
h1 = arr1.zip(i1).to_h
#=> {["a", "b"]=>0, ["c", "d"]=>1, ["e", "f"]=>2}
i1 - arr2.map { |a| h1[a] }
#=> [1, 2]
Note that
arr2.map { |a| h1[a] }
#=> [nil, nil, 0]

What is the best way to access an element from 2d array saved as a hash value?

I have a hash, its values are 2 dimensional arrays, e.g.
hash = {
"first" => [[1,2,3],[4,5,6]],
"second" => [[7,88,9],[6,2,6]]
}
I want to access the elements to print them in xls file.
I did it in this way:
hash.each do |key, value|
value.each do |arr1|
arr1.each do |arr2|
arr2.each do |arr3|
sheet1.row(row).push arr3
end
end
end
end
Is there a better way to access each single element without using each-statement 4 times?
The desired result is to get each value from key-value pair as an array, e.g.
=> [1,2,3,4,5,6] #first loop
=> [7,88,9,6,2,6] #second loop
#and so on
hash = { "first" =>[[1, 2,3],[4,5,6]],
"second"=>[[7,88,9],[6,2,6]] }
hash.values.map(&:flatten)
#=> [[1, 2, 3, 4, 5, 6], [7, 88, 9, 6, 2, 6]]
Isn't it as simple as something like:
hash.each do |k,v|
sheet1.row(row).concat v.flatten
end

ruby uniq on the first two columns of two dimensional array

I have a two dimensional array with three fields in the second dimension.
Is it possible to use uniq on the first two fields of the second dimension?
I have seen array.uniq! {|c| c.first}. When I am correct, this apply uniq on the first field of the array.
Is it possible to use something like array.uniq! {|c| c.first c.second}?
#array = Array.new()
#array << Array.new([journal.from_account_number, journal.from_account,
journal.buchungsart])
There are several entries in #array.
The question was how to get unique values from array not considering journal.buchungsart.
The answer was: #array = #array.uniq! {|c| [c.first, c.second]}
Yes, put the values in an array:
array.uniq! {|c| [c.first, c.second]}
Just return an array of two elements in the block.
Since Array#second is not defined in the standard library, do
array.uniq! { |c| [c[0], c[1]] }
instead, which can be further simplified to array.uniq! { |c| c[0..1] }
You could also make use of the fact that hash keys are unique.
arr = [[1, 2, 3],
[2, 1, 4],
[1, 2, 5]]
a = arr.each_with_object({}) { |row, h| h.update(row.first(2)=>row) }.values
#=> [[1, 2, 5], [2, 1, 4]]
See Hash#update (aka merge!).
Before extracting the hash values with Hash#value, we have computed the following hash.
arr.each_with_object({}) { |row, h| h.update(row.first(2)=>row) }
#=> {[1, 2]=>[1, 2, 5], [2, 1]=>[2, 1, 4]}
Notice that, for given values of the first two elements in a row, it is the last row of arr with those values that is to be "kept". The first such row in arr is to be kept, use the following.
arr.each_with_object({}) { |row, h| h.update(row.first(2)=>row) { |_,o,_| o } }.values
#=> [[1, 2, 3], [2, 1, 4]]
This does not mutate arr. If arr is to be modified, write
arr.replace(a)
where a is defined above.
Array#first accepts a parameter :
%w(a b c d e f).first(2)
# => ["a", "b"]
so you could just use :
array.uniq!{ |c| c.first(2) }

Pushing max hash key-values into an array in Ruby?

I have a hash called count, defined as count = {4=>2, 5=>3, 6=>3, 7=>1}.
I want to take the max value, and then push the key that corresponds to that value into an array, so I do this:
array = []
array.push(count.max_by{|k,v| v}[0])
=>> [5]
However, 6 also has the value 3, which is another maximum value. How do I push this value into the array so I get [5,6] instead of just [5]?
This is the way to choose the max values of the hash:
count.values.max
=> 3
Use the select method on the hash:
count.select{ |k, v| v == count.values.max }
=> {5=>3, 6=>3}
Get the keys:
count.select{ |k, v| v == count.values.max }.keys
=> [5, 6]
And finally assign to an array:
array = count.select{ |k, v| v == count.values.max }.keys
This could probably be improved dramatically, but off the top of my head:
count.group_by{|k,v| v}.max_by{|k,v| k}.last.map(&:first)
First, group the key/value pairs of the hash so that the ones with the same value are in the same groups:
count.group_by{|k,v| v} #=> {2=>[[4, 2]], 3=>[[5, 3], [6, 3]], 1=>[[7, 1]]}
Then get the group with the maximum value:
.max_by{|k,v| k} #=> [3, [[5, 3], [6, 3]]]
Now, we just want the original keys out of that, so first we take the last element of the pair:
.last #=> [[5, 3], [6, 3]]
And we want just the first element of each of those nested pairs:
.map(&:first) #=> [5, 6]
This approach avoids one pass through the map as compared to the select-based solutions. It's probably not a significant performance win unless the data set is really huge, in which case the intermediate data structures being built by my solution will be more of a problem anyway.
max = count.values.max
array = count.keys.select{|k| count[k] == max}

Sort specific items of an array first

I have a ruby array that looks something like this:
my_array = ['mushroom', 'beef', 'fish', 'chicken', 'tofu', 'lamb']
I want to sort the array so that 'chicken' and 'beef' are the first two items, then the remaining items are sorted alphabetically. How would I go about doing this?
irb> my_array.sort_by { |e| [ e == 'chicken' ? 0 : e == 'beef' ? 1 : 2, e ] }
#=> ["chicken", "beef", "fish", "lamb", "mushroom", "tofu"]
This will create a sorting key for each element of the array, and then sort the array elements by their sorting keys. Since the sorting key is an array, it compares by position, so [0, 'chicken'] < [1, 'beef'] < [2, 'apple' ] < [2, 'banana'].
If you don't know what elements you wanted sorted to the front until runtime, you can still use this trick:
irb> promotables = [ 'chicken', 'beef' ]
#=> [ 'chicken', 'beef' ]
irb> my_array.sort_by { |e| [ promotables.index(e) || promotables.size, e ] }
#=> ["chicken", "beef", "fish", "lamb", "mushroom", "tofu"]
irb> promotables = [ 'tofu', 'mushroom' ]
#=> [ 'tofu', 'mushroom' ]
irb> my_array.sort_by { |e| [ promotables.index(e) || promotables.size, e ] }
#=> [ "tofu", "mushroom", "beef", "chicken", "fish", "lamb"]
Mine's a lot more generic and more useful if you get your data only at runtime.
my_array = ['mushroom', 'beef', 'fish', 'chicken', 'tofu', 'lamb']
starters = ['chicken', 'beef']
starters + (my_array.sort - starters)
# => ["chicken", "beef" "fish", "lamb", "mushroom", "tofu"]
Could just do
firsts = ["chicken", "beef"]
[*firsts, *(my_array.sort - firsts)]
#=> ["chicken", "beef", "fish", "lamb", "mushroom", "tofu"]

Resources