Changing the value of one attribute in a Ruby collection - ruby

Say for example I've got a collection like this:
[{"name" => "Ganesh", "magic_number" => 7}, {"name" => "Comrade", "magic_number" => 2}...]
How can I change the value of ALL the magic_numbers in the collection to be the same value (e.g. 8)?
I'm sure it's using something like map or collect but I can't seem to do it at the moment and return me the whole collection with the changes, just one or the other...

Just use .each:
a = [{"name" => "Ganesh", "magic_number" => 7}, {"name" => "Comrade", "magic_number" => 2} ]
a.each { |x| x['magic_number'] = 8 }
# a is now [{"magic_number"=>8, "name"=>"Ganesh"}, {"magic_number"=>8, "name"=>"Comrade"}]
The argument to the block is a reference to the original elements so you can change them as desired. Note that this changes a in-place which I think is what you're after.

This works:
x = [{"name" => "Ganesh", "magic_number" => 7}, {"name" => "Comrade", "magic_number" => 2}]
x.map{|i| i["magic_number"] = 0; i }
=> [{"magic_number"=>0, "name"=>"Ganesh"}, {"magic_number"=>0, "name"=>"Comrade"}]

Related

Ruby : How to sort an array of hash in a given order of a particular key

I have an array of hashes, id being one of the keys in the hashes. I want to sort the array elements according to a given order of ID values.
Suppose my array(size=5) is:
[{"id"=>1. ...}, {"id"=>4. ...}, {"id"=>9. ...}, {"id"=>2. ...}, {"id"=>7. ...}]
I want to sort the array elements such that their ids are in the following order:
[1,3,5,7,9,2,4,6,8,10]
So the expected result is:
[{'id' => 1},{'id' => 7},{'id' => 9},{'id' => 2},{'id' => 4}]
Here is a solution for any custom index:
def my_index x
# Custom code can be added here to handle items not in the index.
# Currently an error will be raised if item is not part of the index.
[1,3,5,7,9,2,4,6,8,10].index(x)
end
my_collection = [{"id"=>1}, {"id"=>4}, {"id"=>9}, {"id"=>2}, {"id"=>7}]
p my_collection.sort_by{|x| my_index x['id'] } #=> [{"id"=>1}, {"id"=>7}, {"id"=>9}, {"id"=>2}, {"id"=>4}]
Then you can format it in any way you want, maybe this is prettier:
my_index = [1,3,5,7,9,2,4,6,8,10]
my_collection.sort_by{|x| my_index.index x['id'] }
I would map the hash based on the values like so:
a = [{"id"=>1}, {"id"=>4}, {"id"=>9}, {"id"=>2}, {"id"=>7}]
[1,3,5,7,9,2,4,6,8,10].map{|x| a[a.index({"id" => x})] }.compact
#=> [{"id"=>1}, {"id"=>7}, {"id"=>9}, {"id"=>2}, {"id"=>4}]
General note on sorting. Use #sort_by method of the ruby's array class:
[{'id' => 1},{'id'=>3},{'id'=>2}].sort_by {|x|x['id'] }
# => [{"id"=>1}, {"id"=>2}, {"id"=>3}]
Or with usage #values method as a callback:
[{'id' => 1},{'id'=>3},{'id'=>2}].sort_by(&:values)
# => [{"id"=>1}, {"id"=>2}, {"id"=>3}]
or you can use more obvious version with #sort method:
[{'id' => 1},{'id'=>3},{'id'=>2}].sort {|x,y| x['id'] <=> y['id'] }
# => [{"id"=>1}, {"id"=>2}, {"id"=>3}]
For your case, to sort with extended condition use #% to split even and odd indexes:
[{'id' => 1},{'id'=>4},{'id'=>9},{'id'=>2},{'id'=>7}].sort do |x,y|
u = y['id'] % 2 <=> x['id'] % 2
u == 0 && y['id'] <=> x['id'] || u
end
# => [{"id"=>1}, {"id"=>7}, {"id"=>9}, {"id"=>2}, {"id"=>4}]
For your case, to sort with extended condition use #% to split according the index, even id value is absent in the index array:
index = [1,3,5,7,4,2,6,8,10] # swapped 2 and 4, 9 is absent
[{'id' => 1},{'id'=>4},{'id'=>9},{'id'=>2},{'id'=>7}].sort do |x,y|
!index.rindex( x[ 'id' ] ) && 1 || index.rindex( x[ 'id' ] ) <=> index.rindex( y[ 'id' ] ) || -1
end
# => [{"id"=>1}, {"id"=>7}, {"id"=>4}, {"id"=>2}, {"id"=>9}]
Why not just sort?
def doit(arr, order)
arr.sort { |h1,h2| order.index(h1['id']) <=> order.index(h2['id']) }
end
order = [1,3,5,7,9,2,4,6,8,10]
arr = [{'id' => 1}, {'id' => 4}, {'id' => 9}, {'id' => 2}, {'id' => 7}]
doit(arr, order)
#=> [{'id' => 1}, {'id' => 7}, {'id' => 9}, {'id' => 2}, {'id' => 4}]
a= [{"id"=>1}, {"id"=>4}, {"id"=>9}, {"id"=>2}, {"id"=>7}]
b=[1,3,5,7,9,2,4,6,8,10]
a.sort_by{|x| b.index (x['id'])}

Selecting single hash from array where one hash element has the highest value

From the following array of hashes, how can I select a single hash where timestamp has the highest value?
data = [
{"id" => 1, "timestamp" => 1383314276, "data" => "bea7c82f-f4b2-492a-aba3-033b1a54d9d0"},
{"id" => 2, "timestamp" => 1383314386, "data" => "64ed2ed9-763d-443f-a74e-e7f10cbe783e"},
{"id" => 3, "timestamp" => 1383314331, "data" => "f8cfaa99-ffe0-4d88-8fac-37e4ce462d3a"}
]
Here you can see:
data.max_by{|e| e["timestamp"] }
# >> {"id"=>2, "timestamp"=>1383314386, "data"=>"64ed2ed9-763d-443f-a74e-e7f10cbe783e"}

Ruby, turn array of hashes into single hash

I have the following Array of Hashes:
a = [{:a => 1, :b => "x"}, {:a => 2, :b => "y"}]
I need to turn it into:
z={"x" => 1, "y" => 2}
or:
z={1 => "x", 2 => "y"}
Can I do this in a clean and functional way?
Something like this:
Hash[a.map(&:values)] # => {1=>"x", 2=>"y"}
if you want the other way:
Hash[a.map(&:values).map(&:reverse)] # => {"x"=>1, "y"=>2}
incorporating the suggestion from #squiguy:
Hash[a.map(&:values)].invert

Ruby loops. Hash into string

I am working with Ruby. I need to grab each key/value and put it into a string.
So far I have:
values = ['first' => '1', 'second' => '2']
#thelink = values.collect do | key, value |
"#{key}=#{value}&"
end
When I print #thelink I see:
first1second2=&
But Really what I want is
first=1&second=2
Could anybody help/explain please?
There is something subtle you are missing here {} vs [].
See the below taken from IRB tests:
irb(main):002:0> {'first' => 1, 'second' => 2}
=> {"second"=>2, "first"=>1}
irb(main):003:0> ['first' => 1, 'second' => 2]
=> [{"second"=>2, "first"=>1}]
irb(main):004:0> {'first' => 1, 'second' => 2}.class
=> Hash
irb(main):005:0> ['first' => 1, 'second' => 2].class
=> Array
Similar to this:
irb(main):006:0> {'first' => 1, 'second' => 2}.collect { |key,value| puts "#{key}:#{value}" }
second:2
first:1
=> [nil, nil]
irb(main):007:0> ['first' => 1, 'second' => 2].collect { |key,value| puts "#{key}:#{value}" }
second2first1:
=> [nil]
The array has a single element (a hash) that, as a string, is everything concatenated. This is the important thing to note here.
On the other hand, the hash iterates by handing you the key/value pairs that you are expecting.
Hope that helps.
I think your code has a typo (a hash is delimited by {} not by []). Try this
values = {'first' => '1', 'second' => '2'}
r = values.map{|k,v| "#{k}=#{v}"}.join('&')
puts r
#shows: first=1&second=2

merge some complex hashes in ruby

I'd like to merge the following hashes together.
h1 = {"201201" => {:received => 2}, "201202" => {:received => 4 }}
h2 = {"201201" => {:closed => 1}, "201202" => {:closed => 1 }}
particularly, my expected result is:
h1 = {"201201" => {:received => 2, :closed => 1}, "201202" => {:received => 4, :closed => 1 }}
I have tried every way as:
h = h1.merge(h2){|key, first, second| {first , second} }
unfortunately, neither seemed to work out fine for me.
any advice would be really appreciated.
This should work for you:
h = h1.merge(h2){|key, first, second| first.merge(second)}

Resources