Ruby: combine two-dimensional arrays into a single array of hashes [closed] - ruby

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
what is the cleanest way to turn these two arrays of arrays:
[[1, 'a'], [2, 'b']]
and
[[1, 'c'], [2, 'd']]
into a single array of hashes with arbitrary keys like this:
[{:id => 1, :foo => 'a', :bar => 'c'}, {:id => 2, :foo => 'b', :bar => 'd'}]
to elaborate, position 0 of every internal array should map to the :id key, position 1 of the internal arrays of the first array should map to the :foo key, and position 1 of the internal arrays of the second array should map to the :bar key. further, the :id key of each hash should not repeat, and each hash should have an :id, :foo, and :bar key.

Clarify by decomposing the block variables:
a = [[1, 'a'], [2, 'b']]
b = [[1, 'c'], [2, 'd']]
a.zip(b).map { |(id,foo),(_,bar)| {id: id, foo: foo, bar: bar } }
#=> [{:id=>1, :foo=>"a", :bar=>"c"}, {:id=>2, :foo=>"b", :bar=>"d"}]

Related

Array of objects or hash in Ruby [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
While working with a hash today in Ruby, I kept coming up against a few complications when sorting and accessing it. In my mind, I needed just a simple array.pop method to do what I needed. being that I just learned about class definitions, I had an idea and wanted to see if there was a reason not to do it this way.
hash = {"a" => 1, "b" => 2, "c" => 3}
Could I not do the same thing, but leave it open to more sorting methods and extensible data if I create this an array of objects containing data values?
Something like this
class Key
attr_accessor :value
def initialize (value)
#value = value
end
end
I'd then create an array of Key objects. I can then sort the array easier than the hash and still get the data from the inside the Keys. I figure this keeps things open for a more extensible bit of code if I find several bits of data need to be held together and sorted through.
Is this bad practice? Can you see a situation this would bite me? Am I solving a problem with a hammer because I just got one?
This ability is built into the Hash and Array data structures.
If you are using key-based access, get the keys and sort them:
hsh = {'a' => 1, 'c' => 3, 'b' => 2}
keys = hsh.keys.sort # keys: ['a', 'b', 'c']
If you need the values sorted, get the values and sort:
values = hsh.values.sort # values: [1, 2, 3]
Hash includes the Enumerable module which gives you all kinds of nifty ways of enumerating and sorting the hash.
irb(main):006:0> h = {'a' => 1, 'c' => 3, 'b' => 2}
=> {"a"=>1, "c"=>3, "b"=>2}
irb(main):007:0> h.sort { |a,b| b<=> a }
=> [["c", 3], ["b", 2], ["a", 1]]
irb(main):010:0> h = { 'three' => 3, 'one' => 1, 'two' => 2 }
=> {"three"=>3, "one"=>1, "two"=>2}
irb(main):011:0> h.sort { |a,b| a[0] <=> b[0] }
=> [["one", 1], ["three", 3], ["two", 2]]
irb(main):012:0> h.sort { |a,b| a[1] <=> b[1] }
=> [["one", 1], ["two", 2], ["three", 3]]
If you are looking for an array of hash values based on some key sorting:
irb(main):016:0> h.keys.sort.map { |key| h[key] }
=> [1, 3, 2]

Ruby, sort array of hashes passed by the an specified hash key [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I want to sort an array of hashes by a passed hash key that is specified as a parameter:
array([{'a' => 2}, {'a' => 1}], 'a') => [{'a' => 1}, {'a' => 2}]]
array([{a: 1}, {a: 5}, {a: 3}], :a) => [{a:1}, {a: 3}, {a: 5}]
Thanks
def array(arr, key)
arr.sort_by { |a| a[key] }
end

Accumulating data in sorted manner in Ruby [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I want to store triples of data associated with key. I have triples of this form:
"data1" "data2" "data3"
where data1 is an integer. I have a mechanism to triples to key. For example "key1" is mapped to
["data1", "data2", "data3"]
There can be multiple triples associated with a key. For example, [4, "data2", "data3"], [1, "data5", "data6"] and [3, "data8", "data9"] may be mapped to "key1". I want these triples to be sorted by the "data1" field and mapped. In this case,
"key1" => {[1, "data5", "data6"] [3, "data8", "data9"] [4, "data2", "data3"]}
How do I do this Ruby?
You can do as below :
hsh = {"key1" => [[4,"data2","data3"], [1, "data5","data6"],[3, "data8","data9"]] }
hsh.each{|k,v| hsh[k]=v.sort_by(&:first)}
p hsh
# >> {"key1"=>[[1, "data5", "data6"], [3, "data8", "data9"], [4, "data2", "data3"]]}
If you don't want to update the source hash,then use #dup.
hsh = {"key1" => [[4,"data2","data3"], [1, "data5","data6"],[3, "data8","data9"]] }
hsh1 = hsh.dup
hsh1.each{|k,v| hsh1[k]=v.sort_by(&:first)}
p hsh1
# >> {"key1"=>[[1, "data5", "data6"], [3, "data8", "data9"], [4, "data2", "data3"]]}

Replace values of one hash with keys of second hash when their values are equal [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
long time appreciator of the Ruby Gurus first time question.
I have two hashes.
hash1 = {"a" =>1, "b"=> 2, "c" => 3}
hash2 = {"f" =>1, "g"=> 2, "h" => 3, "i"=>2, "j"=>1}
I want to replace the values of the of the second hash with the keys of the first hash when their values are equal something like:
hash2 = {"f"=>"a", "g"=>"b", "h"=>"c", "i"=>"b", "j"=>"a"}
The order doesn't matter at all.
hash1_inverted = hash1.invert
hash3 = hash2.map { |k, v| [k, hash1_inverted[v]] }.to_h
#=> {"f"=>"a", "g"=>"b", "h"=>"c", "i"=>"b", "j"=>"a"}
I would do as below :
hash1 = {:a =>1, :b=> 2, :c => 3}
hash2 = {:f =>1, :g=> 2, :h => 3, :i=>2, :j=>1}
hash2.each_with_object({}){|(k,v),h| h[k]=hash1.key(v)}
# => {:f=>:a, :g=>:b, :h=>:c, :i=>:b, :j=>:a}
or
hash1 = {:a =>1, :b=> 2, :c => 3}
nw_hsh = hash1.invert
hash2 = {:f =>1, :g=> 2, :h => 3, :i=>2, :j=>1}
hash2.each_with_object({}){|(k,v),h| h[k]=nw_hsh[v]}
# => {:f=>:a, :g=>:b, :h=>:c, :i=>:b, :j=>:a}

Basic Array Iteration in Ruby

What's a better way to traverse an array while iterating through another array? For example, if I have two arrays like the following:
names = [ "Rover", "Fido", "Lassie", "Calypso"]
breeds = [ "Terrier", "Lhasa Apso", "Collie", "Bulldog"]
Assuming the arrays correspond with one another - that is, Rover is a Terrier, Fido is a Lhasa Apso, etc. - I'd like to create a dog class, and a new dog object for each item:
class Dog
attr_reader :name, :breed
def initialize(name, breed)
#name = name
#breed = breed
end
end
I can iterate through names and breeds with the following:
index = 0
names.each do |name|
Dog.new("#{name}", "#{breeds[index]}")
index = index.next
end
However, I get the feeling that using the index variable is the wrong way to go about it. What would be a better way?
dogs = names.zip(breeds).map { |name, breed| Dog.new(name, breed) }
Array#zip interleaves the target array with elements of the arguments, so
irb> [1, 2, 3].zip(['a', 'b', 'c'])
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
You can use arrays of different lengths (in which case the target array determines the length of the resulting array, with the extra entries filled in with nil).
irb> [1, 2, 3, 4, 5].zip(['a', 'b', 'c'])
#=> [ [1, 'a'], [2, 'b'], [3, 'c'], [4, nil], [5, nil] ]
irb> [1, 2, 3].zip(['a', 'b', 'c', 'd', 'e'])
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
You can also zip more than two arrays together:
irb> [1,2,3].zip(['a', 'b', 'c'], [:alpha, :beta, :gamma])
#=> [ [1, 'a', :alpha], [2, 'b', :beta], [3, 'c', :gamma] ]
Array#map is a great way to transform an array, since it returns an array where each entry is the result of running the block on the corresponding entry in the target array.
irb> [1,2,3].map { |n| 10 - n }
#=> [ 9, 8, 7 ]
When using iterators over arrays of arrays, if you give a multiple parameter block, the array entries will be automatically broken into those parameters:
irb> [ [1, 'a'], [2, 'b'], [3, 'c'] ].each { |array| p array }
[ 1, 'a' ]
[ 2, 'b' ]
[ 3, 'c' ]
#=> nil
irb> [ [1, 'a'], [2, 'b'], [3, 'c'] ].each do |num, char|
...> puts "number: #{num}, character: #{char}"
...> end
number 1, character: a
number 2, character: b
number 3, character: c
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
Like Matt Briggs mentioned, #each_with_index is another good tool to know about. It iterates through the elements of an array, passing a block each element in turn.
irb> ['a', 'b', 'c'].each_with_index do |char, index|
...> puts "character #{char} at index #{index}"
...> end
character a at index 0
character b at index 1
character c at index 2
#=> [ 'a', 'b', 'c' ]
When using an iterator like #each_with_index you can use parentheses to break up array elements into their constituent parts:
irb> [ [1, 'a'], [2, 'b'], [3, 'c'] ].each_with_index do |(num, char), index|
...> puts "number: #{num}, character: #{char} at index #{index}"
...> end
number 1, character: a at index 0
number 2, character: b at index 1
number 3, character: c at index 2
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
each_with_index leaps to mind, it is a better way to do it the way you are doing it. rampion has a better overall answer though, this situation is what zip is for.
This is adapted from Flanagan and Matz, "The Ruby Programming Language", 5.3.5 "External Iterators", Example 5-1, p. 139:
++++++++++++++++++++++++++++++++++++++++++
require 'enumerator' # needed for Ruby 1.8
names = ["Rover", "Fido", "Lassie", "Calypso"]
breeds = ["Terrier", "Lhasa Apso", "Collie", "Bulldog"]
class Dog
attr_reader :name, :breed
def initialize(name, breed)
#name = name
#breed = breed
end
end
def bundle(*enumerables)
enumerators = enumerables.map {|e| e.to_enum}
loop {yield enumerators.map {|e| e.next} }
end
bundle(names, breeds) {|x| p Dog.new(*x) }
+++++++++++++++++++++++++++++++++++++++++++
Output:
#<Dog:0x10014b648 #name="Rover", #breed="Terrier">
#<Dog:0x10014b0d0 #name="Fido", #breed="Lhasa Apso">
#<Dog:0x10014ab80 #name="Lassie", #breed="Collie">
#<Dog:0x10014a770 #name="Calypso", #breed="Bulldog">
which I think is what we wanted!
As well as each_with_index (mentioned by Matt), there's each_index. I sometimes use this because it makes the program more symmetrical, and therefore wrong code will look wrong.
names.each_index do |i|
name, breed = dogs[i], breeds[i] #Can also use dogs.fetch(i) if you want to fail fast
Dog.new(name, breed)
end

Resources