Hash of hashes from an array - ruby

From an array:
this = [1, 2, 3, 4, 5]
I am trying to create a hash of hashes:
{{num: 1}, {num: 2}, {num: 3}, {num: 4}, {num: 5}}
But I'm getting an empty hash:
Hash.new(this.each do |num| Hash.new(num: num) end)
# => {}
What am I doing wrong?

First, your desired result in your question doesn't make sense since you're using the Hash {} syntax, but there are no keys. It seems as though you want your result to be an array of hashes.
Second, you're confusing each with map. each simply iterates through an array, passing each item to the block. The return value of arr.each is just arr. map, on the other hand, returns a new array based on the return value of the block:
[1, 2, 3, 4, 5].map { |item| { num: item } }

You are setting the default value (furthermore with a block that does not do anything meaningful) without setting any key-value pairs.

Related

Ruby's array min returning value, not array

In Ruby (3.0.1) the min function on an array
Returns one of the following:
The minimum-valued element from self.
A new Array of minimum-valued elements selected from self.
(from here).
So, given
l = [{n: 1, m: 6}, {n: 1, m: 5}, {n: 2, m: 4}, {n: 3, m: 3}, {n: 4, m: 3}]
I would expect
l.min { |a, b| a[:n] <=> b[:n] }
=> [{:n=>1, :m=>6}, {:n=>1, :m=>5}]
but instead I get
l.min { |a, b| a[:n] <=> b[:n] }
=> {:n=>1, :m=>6}
Why? Why am I getting one of the list of the minimal elements rather than the entire list of minimal elements?
If you read the rest of the specification:
With no argument and (a block/no block), returns the element in self having the minimum value per (the block/method <=>):
The only case when it returns more than one element is if you specify the number of elements that you want returned:
With an argument n and (a block/no block), returns a new Array with at most n elements, in ascending order per (the block/method <=>):
[0, 1, 2, 3].min(3) # => [0, 1, 2]

Convert Array of Hashes to a Hash

I'm trying to convert the following:
dep = [
{id: 1, depen: 2},
{id: 1, depen: 3},
{id: 3, depen: 4},
{id: 5, depen: 3},
{id: 3, depen: 6}
]
Into a single hash:
# {1=>2, 1=>3, 3=>4, 5=3, 3=>6}
I tried a solution I found on another question:
dep.each_with_object({}) { |g,h| h[g[:id]] = g[:dep_id] }
However, the output removed elements and gave me:
#{1=>3, 3=>6, 5=>2}
where the last element is also incorrect.
You cannot have a hash like {1=>2, 1=>3, 3=>4, 5=3, 3=>6}. All keys of a hash mst have be unique.
If you want to get a hash mapping each id to a list of dependencies, you can use:
result = dep.
group_by { |obj| obj[:id] }.
transform_values { |objs| objs.map { |obj| obj[:depen] } }
Or
result = dep.reduce({}) do |memo, val|
memo[val[:id]] ||= []
memo[val[:id]].push val[:depen]
memo
end
which produce
{1=>[2, 3], 3=>[4, 6], 5=>[3]}

Why does uniq return original value of array after map in Ruby?

I tried the following code:
numbers = [1,2,2,3,4]
numbers.map.uniq {|number| number < 2 }
My understanding is that the return value of map is passed to uniq. I expected:
[true, false]
Instead, I received:
[1, 2]
It seems that uniq maintains a reference to the original array.
Could someone provide insight into this behaviour?
Array#uniq accepts a block, defining the condition on what should be treated uniq.
main > numbers = [1,2,2,3,4].map
#⇒ #<Enumerator: ...>
main > numbers.uniq
#⇒ [1, 2, 3, 4]
# effectively the same as
main > numbers.to_a.uniq
#⇒ [1, 2, 3, 4]
main > numbers.uniq { |number| number.odd? }
#⇒ [1, 2]
The latter returns one odd and one non-odd (even) element. In your case it returns 1 element that is less than 2 and one element that is greater or equal to two.
Note, that map enumerator is effectively there:
numbers.each &Math.method(:sqrt)
#⇒ [1.0, 1.4142135623730951, 1.4142135623730951,
# 1.7320508075688772, 2.0]
You're not actually doing anything with the map call, your function is roughly equivalent to this:
[1,2,2,3,4].uniq {|number| p number < 2 }
Methods like map return an Enumerable type, and you are then calling uniq on that Enumerable. From the Ruby docs:
If no block is given, an Enumerator is returned instead.
Effectively your map is a no-op.
I think you're also misunderstanding the uniq method. Uniq is going to filter out any elements from an array that aren't unique (eg: [1, 1, 2, 3, 3, 4, 5].uniq == [1, 2, 3, 4, 5]), not return whether the element is unique (true or false) in the array.
numbers.uniq.map { |number| number < 2 }
uniq method
uniq → new_ary click to toggle source uniq {|item| ...} → new_ary
Returns a new array by removing duplicate values in self.
If a block is given, it will use the return value of the block for
comparison.
It compares values using their hash and eql? methods for efficiency.
self is traversed in order, and the first occurrence is kept.
a = [ "a", "a", "b", "b", "c" ]
a.uniq # => ["a", "b", "c"]
b = [["student","sam"], ["student","george"], ["teacher","matz"]]
b.uniq {|s| s.first} # => [["student", "sam"], ["teacher", "matz"]]
You can read more about uniq method here.

Ruby - Grab Most Pairs From a Hash

hsh = { one: 1, two: 2, three: 3, four: [1, 3, 1, 4] }
How do i grab all key/value pairs from the hash except for where the value is an array? Or, how do i grab all key/value pairs from the hash except for where the key is :four?
It is not clear what you mean by "grab". To get a hash, do the following:
hsh.reject{|k, v| v.kind_of?(Array)}
hsh.reject{|k, v| k == :four}

Indexing an array with duplicate elements before and after sorting

This is the basic problem: I have an array of integers with possibly duplicate elements. I need to know the indices of each element, but when I sort the array, whenever I select an element from the new array, I want to be able to reference the same element from the original array.
I am looking for a solution to the problem, or maybe a solution to the approach I am taking.
Here is an array
a = [1, 2, 3, 4, 3, 5, 2]
There are two 2's and two 3's, but if I'm working with the first 2 (from the left), I want to work with index 1, and if I'm working with the second 2, I want to be working with index 6. So I use a helper array to allow me to do this:
helper = [0, 1, 2, 3, 4, 5, 6]
Which I will iterate over and use to access each element from a.
I could have accomplished this with each_with_index, but the problem begins when I sort the array.
Now I have a sort order
sort_order = [2, 4, 1, 5, 3]
I use sort_by to sort a according to sort_order, to produce
sorted_a = [2, 2, 4, 1, 5, 3, 3]
You may assume all elements in the input exist in sort_order to avoid sort_by exceptions.
Now the problem is that my helper array should be updated to match the new positions. Each element should be sorted the same way as a was sorted, because it is unclear whether the first 2 in the new array was at index 1 or at index 6 of the original array.
So my new helper array might look like
new_helper = [1, 6, 3, 0, 5, 2, 4]
So if I were to go with this approach, how would I produce the new_helper array, given the original array and the sort order?
Maybe there is a better way to do this?
I would suggest first zip the original array with the helper array, sort the zipped array according the component coming from the original array, then unzip them (this method does not exist unfortunately, but you can do transpose). Or you can implement your own sorting logic as pointed out by Hunter.
Make a list of pairs of the original data and that data's index. Like this:
a = [(1, 0), (2, 1), (3, 2), (4, 3), (3, 4), (5, 5), (2,6)]
Sort that list (lexicographically, or just ignore the second part of the pair except to carry it along). The second item in every pair tells you where the element was in the original array.
You need to swap the values in the helper array when you swap then in your main array.
loop do
swapped = false
0.upto(list.size-2) do |i|
if list[i] > list[i+1]
list[i], list[i+1] = list[i+1], list[i] # swap values
helper[i], helper[i+1] = helper[i+1], helper[i]; #swap helper values
swapped = true
end
end
break unless swapped
end
Example
irb(main):001:0> def parallel_sort(list, helper)
irb(main):002:1> loop do
irb(main):003:2* swapped = false
irb(main):004:2> 0.upto(list.size-2) do |i|
irb(main):005:3* if list[i] > list[i+1]
irb(main):006:4> list[i], list[i+1] = list[i+1], list[i] # swap values
irb(main):007:4> helper[i], helper[i+1] = helper[i+1], helper[i]; #swap helper values
irb(main):008:4* swapped = true
irb(main):009:4> end
irb(main):010:3> end
irb(main):011:2> break unless swapped
irb(main):012:2> end
irb(main):013:1> return [list, helper]
irb(main):014:1> end
=> nil
irb(main):015:0> a = [3,2,1]
=> [3, 2, 1]
irb(main):016:0> b = ["three","two","one"]
=> ["three", "two", "one"]
irb(main):017:0> parallel_sort(a,b)
=> [[1, 2, 3], ["one", "two", "three"]]
irb(main):018:0>
Sorting inside a loop is rarely a good idea.... If you are doing so, you might be better off with a treap (fast on average but infrequently an operation will take a while) or red-black tree (relatively slow, but gives pretty consistent operation times). These are rather like hash tables, except they're not as fast, and they keep elements stored in order using trees.
Either way, why not use a class that saves both the value to sort by, and the helper value? Then they're always together, and you don't need a custom sorting algorithm.
Since you have sort_order, your array is already kind of sorted, so we should use this fact as an advantage. I came up with this simple solution:
a = [1, 2, 3, 4, 3, 5, 2]
sort_order = [2, 4, 1, 5, 3]
# Save indices
indices = Hash.new { |hash, key| hash[key] = [] }
a.each_with_index { |elem, index| indices[elem] << index }
# Sort the array by placing elements into "right" positions
sorted = []
helper = []
sort_order.each do |elem|
indices[elem].each do |index|
sorted << elem
helper << index
end
end
p sorted
p helper
The algorithm is based on idea of Counting sort, I slightly modified it to save indices.

Resources