Sort hash by key as a reverse order, return hash in Ruby - ruby

Can you please help anyone?. I have a hash value as following. I need to sort the hash in reverse order by key value and return as also hash type.
My actual expected output is (hash)
result = {20111104111221=>[4, 5, 6], 20111104111220=>[7, 8, 9], 20111104110950=>[1, 2, 3]}
# this is my input
irb(main):096:0> h = {20111104111221=>[4, 5, 6], 20111104110950=>[1, 2, 3], 20111104111220=>[7, 8, 9]}
=> {20111104111221=>[4, 5, 6], 20111104110950=>[1, 2, 3], 20111104111220=>[7, 8, 9]}
# It's not correct
irb(main):095:0> Hash[h.sort]
=> {20111104111221=>[4, 5, 6], 20111104110950=>[1, 2, 3], 20111104111220=>[7, 8, 9]}
So, i tried this. It's correct but it's return as a array value, i need a return value as Hash.
# It's correct but it's not a hash
irb(main):092:0> arr = h.sort_by { |k,v| k }.reverse
=> [[20111104111221, [4, 5, 6]], [20111104111220, [7, 8, 9]], [20111104110950, [1, 2, 3]]]
Again i tried array to hash conversion.. but it's help.
# It's also not correct.
irb(main):092:0> irb(main):098:0> Hash[*arr.flatten]
=> {5=>6, 20111104111221=>4, 20111104110950=>1, 2=>3, 8=>9, 20111104111220=>7}

You're almost there with h.sort_by..., don't flatten that just feed it to Hash[]:
Hash[h.sort_by { |k,v| -k }]
I also simplified your sorting, negating the number does the same thing as sorting and reversing.
For example:
>> h = {20111104111221=>[4, 5, 6], 20111104110950=>[1, 2, 3], 20111104111220=>[7, 8, 9]}
=> {20111104111221=>[4, 5, 6], 20111104110950=>[1, 2, 3], 20111104111220=>[7, 8, 9]}
>> Hash[h.sort_by { |k,v| -k }]
=> {20111104111221=>[4, 5, 6], 20111104111220=>[7, 8, 9], 20111104110950=>[1, 2, 3]}

Which version of ruby are you using? In 1.8 hashes cannot be ordered, in 1.9 they are ordered based on insertion. Here's more info on that.
Given the variation in how they're handled I wouldn't focus on sorting the hash itself, but on sorting your keys and using them as a reference, something like:
data = {20111104111221=>[4, 5, 6], 20111104111220=>[7, 8, 9], 20111104110950=>[1, 2, 3]}
keys = data.keys.sort.reverse
keys.each do |key|
puts data[key].pretty_inspect
# ... do work ...
end
The debug line is in there just as an example of how to access your values. Hope this helps!

Related

Repeating a loop when it reaches the end

I am trying to conceptualize the iteration of two loops
numbers_array = [1,2,3,4,5,6,7,8,9,10]
add_to_array = [1,2,3,4]
While the numbers_array iterates, add_to_array iterates simultaneously adding both elements together at the same time. The caveat is once add_to_array reaches the end, it starts over adding its element to the next index in numbers_array. So at numbers_array[4] we would be adding add_to_array[0] then adding numbers_array[5] to add_to_array[1] and so on. This process would repeat until we reach the end of the numbers_array.
Any input would be greatly appreciated!
You are looking for Enumerable#zip and Enumerable#cycle:
numbers_array = [1,2,3,4,5,6,7,8,9,10]
#⇒ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
add_to_array = [1,2,3,4]
#⇒ [1, 2, 3, 4]
numbers_array.zip(add_to_array.cycle)
#⇒ [[1, 1], [2, 2], [3, 3], [4, 4], [5, 1],
# [6, 2], [7, 3], [8, 4], [9, 1], [10, 2]]
Now do whatever you want with the array returned. E.g. to reduce the zipped result summing elements, map ro Enumerable#sum:
numbers_array.zip(add_to_array.cycle).map(&:sum)
#⇒ [2, 4, 6, 8, 6, 8, 10, 12, 10, 12]
It works by using the % operator to cycle through the indexes.
numbers_array = [1,2,3,4,5,6,7,8,9,10]
add_to_array = [1,2,3,4]
numbers_array.map.with_index do |n, i|
n + add_to_array[i % add_to_array.length]
end
A cool method that's similar, if you didn't want to start over at the next array, would be .zip
https://apidock.com/ruby/Array/zip
add_to_array.zip(*numbers_array.each_slice(add_to_array.size)).
map { |a| a.sum { |e| e.to_i } }
#=> [16, 20, 13, 16]
e.to_i is needed to convert nil values to zeros. See NilClass#to_i.
Another option:
numbers_array.map { |e| e + add_to_array.rotate!.last }
# => [2, 4, 6, 8, 6, 8, 10, 12, 10, 12]
Drawback: add_to_array is mutated by rotate!

Find and return the longest array in a nested array with its size

I want to write a function that takes in a nested array and return the size of the longest array and itself.
max_with_size([]) # [0, []]
max_with_size([2,3,4]) # [3, [2, 3, 4]]
max_with_size([1,[2,3,4]]) # [3, [2, 3, 4]]
max_with_size([[5,[6],[7,8,9],10,11]]) # [5, [5, [6], [7, 8, 9], 10, 11]]
max_with_size([[1,[2,3,4]],[[[5,[6],[7,8,9],10,11]]]]) # [5, [5, [6], [7, 8, 9], 10, 11]]
So far I've got this
def max_with_size (ary)
max_size = ary.size
max_ary = ary
ary.each { |elem|
if elem.is_a? Array
if elem.size > max_size
max_size = max_with_size(elem)[0]
max_ary = max_with_size(elem)[1]
end
end
}
[max_size, max_ary]
end
It works fine for the first 4 cases, but the 5th fails and only delivers this
max_with_size([[1,[2,3,4]],[[[5,[6],[7,8,9],10,11]]]]) # [2, [[1, [2, 3, 4]], [[[5, [6], [7, 8, 9], 10, 11]]]]]
How can I achieve the wanted result?
The following code should print the desired result. I explained code with Inline comments.
#Initialize #max to empty array, #max is an array with two elements, like this: [max_array_size, max_array]
#max = []
def max_with_size(array)
# when #max is empty or when array size is greater than what is store in #max, store array size and array contents in #max
(#max = [array.size, array]) if #max.empty? || (#max[0] < array.size)
#Iterate through each element in array
array.each do |x|
#Skip to next element if x is not an array
next unless x.is_a? Array
#Recursively find max of array x
max_with_size(x)
end
#max
end
Code
def max_arr(arr)
[arr, *arr.each_with_object([]) {|e,a| a << max_arr(e) if e.is_a?(Array) && e.any?}].
max_by(&:size)
end
Examples
examples = [[],
[2,3,4],
[1,[2,3,4]],
[[5,[6],[7,8,9],10,11]],
[[1,[2,3,4]],[[[5,[6],[7,8,9],10,11]]]],
[1, [2, [3, 4, [6, 7, 8, 9, 10], [11, 12]], 13]]]
examples.each do |arr|
a = max_arr(arr)
puts "\n#{arr}\n \#=> #{a.size}, #{a}"
end·
[]
#=> 0, []
[2, 3, 4]
#=> 3, [2, 3, 4]
[1, [2, 3, 4]]
#=> 3, [2, 3, 4]
[[5, [6], [7, 8, 9], 10, 11]]
#=> 5, [5, [6], [7, 8, 9], 10, 11]
[[1, [2, 3, 4]], [[[5, [6], [7, 8, 9], 10, 11]]]]
#=> 5, [5, [6], [7, 8, 9], 10, 11]
[1, [2, [3, 4, [6, 7, 8, 9, 10], [11, 12]], 13]]
#=> 5, [6, 7, 8, 9, 10]

Ruby, perform operation on an array and return the new array, aswell as "changes"

I am looking for a way to perform a certain operation (for instance delete_if) on an array and return both the deleted elements, and the remaining elements.
For example
a = [1,2,3,4,5,6,7,8,9,10]
a.delete_if {|x| x.even? } #=> [[1, 3, 5, 7, 9]]
But what I am looking for is something like
a = [1,2,3,4,5,6,7,8,9,10]
a.some_operation #=> [[1,3,5,7,9],[2,4,6,8,10]]
How would I go about doing this?
Using Enumerable#partition:
a = [1,2,3,4,5,6,7,8,9,10]
a.partition &:even?
# => [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]
The first element of the Enumerable#partition return value contains the elements that are evaluated to true in the block. So you need to use odd? to get what you want.
a.partition &:odd?
# => [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]
You might be looking for something like this:
a = [1,2,3,4,5,6,7,8,9,10]
a.group_by { |x| x.even? }.values
#=> [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]

Sum arrays by index using functional programming

I have several equally sized arrays containing numbers (matrix), and I want to sum them all by their index (matrix columns).
For example, if I have:
data = [[1, 2, 3, 4], [5, 6, 7, 8]]
I want to get the result:
column_totals = [6, 8, 10, 12]
I understand how to do this imperatively, but how would I do this using functional programming? (Preferably, using built in Enumerable methods.) I wasn't very happy with any of the functional solutions I came up with.
I ended up using the Matrix class:
require 'matrix'
data = [[1, 2, 3, 4], [5, 6, 7, 8]]
matrix = Matrix[*data]
# Added sum method to Vector class.
matrix.column_vectors.map { |column| column.sum }
I'm happy enough with that solution, but am frustrated that I couldn't wrap my mind around a good functional solution without relying on the Matrix class.
Specifically, I was tripped up on the step to transform this:
data = [[1, 2, 3, 4], [5, 6, 7, 8]]
into this:
columns = [[1, 5], [2, 6], [3, 7], [4, 8]]
Any reason to not use Array#transpose?
data.transpose
# => [[1, 5], [2, 6], [3, 7], [4, 8]]
Alternatively, if you only want to use Enumerable methods to iterate, you can do
columns = data.inject(Array.new(data.first.length){[]}) { |matrix,row|
row.each_with_index { |e,i| matrix[i] << e }
matrix }
# => [[1, 5], [2, 6], [3, 7], [4, 8]]
or
columns = data.flatten.group_by.with_index { |e,i| i % data[0].size }.values
# => [[1, 5], [2, 6], [3, 7], [4, 8]]
To sum:
columns.map { |row| row.inject :+ }
# => [6, 8, 10, 12]
Thirdly, if you don't need the intermediate columns:
data.inject { |s,r| s.zip(r).map { |p| p.inject :+ } }
# => [6, 8, 10, 12]
You could use Array#transpose, as #Matt hinted, and then sum the arrays inside:
data.transpose.map {|a| a.reduce(:+) }

Iterate over array of array

I have an array of arrays like the following:
=> [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]
I want to rearrange it by order of elements in the inner array, e.g.:
=> [[1,6,11],[2,7,12],[3,8,13],[4,9,14],[5,10,15]]
How can I achieve this?
I know I can iterate an array of arrays like
array1.each do |bla,blo|
#do anything
end
But the side of inner arrays isn't fixed.
p [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]].transpose
#=> [[1, 6, 11], [2, 7, 12], [3, 8, 13], [4, 9, 14], [5, 10, 15]]
use transpose method on Array
a = [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]
a.transpose
#=> [[1, 6, 11], [2, 7, 12], [3, 8, 13], [4, 9, 14], [5, 10, 15]]
Note that this only works if the arrays are of all the same length.
If you want to handle transposing arrays that have different lengths to each other, something like this should do it
class Array
def safe_transpose
max_size = self.map(&:size).max
self.dup.map{|r| r << nil while r.size < max_size; r}.transpose
end
end
and will yield the following
a = [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15,16]]
a.safe_transpose
#=> [[1, 6, 11], [2, 7, 12], [3, 8, 13], [4, 9, 14], [5, 10, 15], [nil, nil, 16]]

Resources