I have an array of arrays:
data = [
["Smith", "Bob", "Male"],
["Jim", "Tim", "Male"],
["Welch", "Anne", "Female"]
]
How would I convert it to look like:
data = [
{:first_name => "Smith", :last_name => "Bob", :gender => "Male"},
{:first_name => "Jim", :last_name => "Tim", :gender => "Male"},
{:first_name => "Welch", :last_name => "Anne", :gender => "Female"}
]
You can do something like this:
fields = [:first_name, :last_name, :gender]
data.map {|row| fields.zip(row).to_h }
#=> [{:first_name=>"Smith", :last_name=>"Bob", :gender=>"Male"}, {:first_name=>"Jim", :last_name=>"Tim", :gender=>"Male"}, {:first_name=>"Welch", :last_name=>"Anne", :gender=>"Female"}]
Keep in mind that this will only work if the elements are in the same order as the fields.
Also you could use Struct:
presenter = Struct.new(:first_name, :last_name, :gender)
data.map { |e| presenter.new(*e).to_h }
#=> [{:first_name=>"Smith", :last_name=>"Bob", :gender=>"Male"},
# {:first_name=>"Jim", :last_name=>"Tim", :gender=>"Male"},
# {:first_name=>"Welch", :last_name=>"Anne", :gender=>"Female"}]
fields = [:first_name, :last_name, :gender]
data.map{|d| Hash[fields.zip(d)]}
Related
Here the array of hashes:
array = [
{:ID => "aaa", :phase => "alpha", :quantity => 80},
{:ID => "aaa", :phase => "beta", :quantity => 160}
]
I'm trying to transform the value of :phase into a key and to assign its value taking the value of :quantity, as follow:
array = [
{:ID => "aaa", :"alpha" => 80},
{:ID => "aaa", :"beta" => 160}
]
Thanks!
I would do it like this:
array = [
{:ID => "aaa", :phase => "alpha", :quantity => 80},
{:ID => "aaa", :phase => "beta", :quantity => 160}
]
array.map { |hash| { :ID => hash[:ID], hash[:phase].to_sym => hash[:quantity] } }
#=> [{:ID=>"aaa", :alpha=>80}, {:ID=>"aaa", :beta=>160}]
You can do it like this:
array.map do |hash|
new_key = hash.delete(:phase)
quantity = hash.delete(:quantity)
hash[new_key] = quantity
hash
end
In an array of hash how to delete an element with particular value for a key?
For example:
array = [ {"lang"=> 'Ruby', "is_using"=> "Yes"}, { "lang"=> "Go", "is_using" => "No"}, {"lang"=> "Rust", "is_using"=> "No"} ]
I need to write a minimal and efficient ruby script which deletes all the elements from the array which has "No" as a value for the key "is_using".
Use Array#delete_if:
array = [ {"lang"=> 'Ruby', "is_using"=> "Yes"}, { "lang"=> "Go", "is_using" => "No"}, {"lang"=> "Rust", "is_using"=> "No"} ]
array.delete_if { |hash| hash['is_using'] == 'No' }
#=> [{ "lang" => "Ruby", "is_using" => "Yes" }]
If you don't want to mutate the original array, then you could use reject:
array = [{ "lang"=> 'Ruby', "is_using"=> "Yes" },
{ "lang"=> "Go", "is_using" => "No" },
{ "lang"=> "Rust", "is_using"=> "No" }]
array.reject { |h| h["is_using"].eql?('Yes') }
# [{"lang"=>"Go", "is_using"=>"No"}, {"lang"=>"Rust", "is_using"=>"No"}]
I have an array
animals = [
[{"name" => "Alex", "spices" => "dog", "vname" => "colour", "value" => "black"},
{"name" => "Alf", "spices" => "dog", "vname" => "colour", "value" => "white"},
{"name" => "Sonia", "spices" => "dog", "vname" => "colour", "value" => "white"}],
[{"name" => "Alex", "spices" => "dog", "vname" => "health", "value" => "80"},
{"name" => "Alf", "spices" => "dog", "vname" => "health", "value" => "98"}],
[{"name" => "Alex", "spices" => "dog", "vname" => "age", "value" => "12"}]
]
Every animal (Alex, Alf and Sonia) is described by colour, health and age (vname values), but Alf does not have his hash with age in third subarray and Sonia does not have her hashes with health (second subarray) and age (third subarray). I want to check if any subarray does not have "vname" pair for some dog and if doesn't to add hash like this
{"name" => "Alf", "spices" => "dog", "vname" => "age", "value" => "unknown"}
to get an array
animals = [
[{"name" => "Alex", "spices" => "dog", "vname" => "colour", "value" => "black"},
{"name" => "Alf", "spices" => "dog", "vname" => "colour", "value" => "white"},
{"name" => "Sonia", "spices" => "dog", "vname" => "colour", "value" => "white"}],
[{"name" => "Alex", "spices" => "dog", "vname" => "health", "value" => "80"},
{"name" => "Alf", "spices" => "dog", "vname" => "health", "value" => "98"},
{"name" => "Sonia", "spices" => "dog", "vname" => "health", "value" => "unknown"}],
[{"name" => "Alex", "spices" => "dog", "vname" => "age", "value" => "12"},
{"name" => "Alf", "spices" => "dog", "vname" => "age", "value" => "unknown"},
{"name" => "Sonia", "spices" => "dog", "vname" => "age", "value" => "unknown"}]
]
Can You help me with this?
Umm. Set the names in an Array. Loop through your animals array and for each of the array inside, check if has hash for all animal names. Add the default hash for those that are missing.
animal_names = ["Alex", "Alf", "Sonia"]
animals.each do |animals_by_vname|
vname = animals_by_vname.first["vname"]
names_present = animals_by_vname.map {|i| i["name"]}
names_missing = animal_names - names_present
names_missing.each do |name|
animals_by_vname << {
"name" => name,
"spices" => "dog",
"vname" => vname,
"value" => "unknown"
}
end
end
puts animals # should be you wanted it.
That solves your problem.
However, I honestly believe, the data structure needs to be optimised. Maybe have a hash of arrays with animal names as keys. Like:
{
"Alex": { health: "good" }
"Alf": { age: 10, health: "good" }
}
Your data format is very hard to work with.
You could define an Animal class, keep an Animal.all hash and write your data in this structure:
data = [
[{ 'name' => 'Alex', 'spices' => 'dog', 'vname' => 'colour', 'value' => 'black' },
{ 'name' => 'Alf', 'spices' => 'dog', 'vname' => 'colour', 'value' => 'white' },
{ 'name' => 'Sonia', 'spices' => 'dog', 'vname' => 'colour', 'value' => 'white' }],
[{ 'name' => 'Alex', 'spices' => 'dog', 'vname' => 'health', 'value' => '80' },
{ 'name' => 'Alf', 'spices' => 'dog', 'vname' => 'health', 'value' => '98' }],
[{ 'name' => 'Alex', 'spices' => 'dog', 'vname' => 'age', 'value' => '12' }]
]
class Animal
attr_accessor :name, :species, :health, :age, :colour
#all = {}
class << self
attr_reader :all
def find_or_create_by_name(params)
all[params['name']] ||= Animal.new(params)
end
end
def initialize(params)
#name = params['name']
#species = params['spices']
end
def to_h
{
name: name,
species: species,
age: age || 'unknown',
colour: colour || 'unknown',
health: health || 'unknown'
}
end
def to_s
to_h.to_s
end
alias inspect to_s
end
data.each do |info|
info.each do |params|
animal = Animal.find_or_create_by_name(params)
animal.instance_variable_set("##{params['vname']}", params['value'])
end
end
require 'pp'
pp Animal.all
# {"Alex"=>
# {:name=>"Alex", :species=>"dog", :age=>"12", :colour=>"black", :health=>"80"},
# "Alf"=>
# {:name=>"Alf", :species=>"dog", :age=>"unknown", :colour=>"white", :health=>"98"},
# "Sonia"=>
# {:name=>"Sonia", :species=>"dog", :age=>"unknown", :colour=>"white", :health=>"unknown"}}
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 6 years ago.
Improve this question
I have this hash:
{"id" => [323, 324], info => ["Test Info", "Test Info2"]}
I would like to have a hash inside an array like this:
array = [
[{"id" => "323", info => "Test Info"}],
[{"id" => "324", info => "Test Info2"}]
]
I have duplicate data. I need to use uniq on id and info to get each id only once. Because of that, I need to join them afterwards.
h = {"id" => [323, 324], "info" => ["Test Info", "Test Info2"]}
h.map { |k, v| [k].product v }.transpose.map &:to_h
#⇒ [
# [0] {
# "id" => 323,
# "info" => "Test Info"
# },
# [1] {
# "id" => 324,
# "info" => "Test Info2"
# }
]
If you want to wrap each nested hash into it’s own array, one more action is required:
h.map { |k, v| [k].product v }.transpose.map(&:to_h).map { |e| [e] }
#⇒ [
# [0] [
# [0] {
# "id" => 323,
# "info" => "Test Info"
# }
# ],
# [1] [
# [0] {
# "id" => 324,
# "info" => "Test Info2"
# }
# ]
# ]
You can use this solution (maybe so OP in this case)
h = {"id" => [323, 324], "info" => ["Test Info", "Test Info2"]}
h.values.transpose.map do |a|
[Hash[h.keys.each_with_index.map { |k, i| [k, a[i]] }]]
end
# => [[{"id"=>323, "info"=>"Test Info"}], [{"id"=>324, "info"=>"Test Info2"}]]
This looks like a job for zip which zippers together two arrays:
hash = {
"id" => [323, 324],
"info" => ["Test Info", "Test Info2"]
}
keys = %w[ id info ]
array = hash['id'].zip(hash['info']).map do |id, info|
Hash[keys.zip(pair)]
end
# => [{"id"=>323, "info"=>"Test Info"}, {"id"=>324, "info"=>"Test Info2"}]
The Hash[] method is used to convert an array of form [['a',1],['b',2]] to a hash of form {'a'=>1,'b'=>2}.
Here are a couple more:
h = {"id" => [323, 324], "info" => ["Test Info", "Test Info2"]}
da_keys = h.keys
h.values.transpose.each_with_object([]) { |a,b| b << da_keys.zip(a).to_h }
#=> [{"id"=>323, "info"=>"Test Info"}, {"id"=>324, "info"=>"Test Info2"}]
rolling_keys = h.keys.cycle
h.values.transpose.map {|a| a.each_with_object({}) {|v,g| g.update(rolling_keys.next=>v)}}
#=> [{"id"=>323, "info"=>"Test Info"}, {"id"=>324, "info"=>"Test Info2"}]
You asked for this:
array = [
[{"id" => "323", info => "Test Info"}],
[{"id" => "324", info => "Test Info2"}]
]
But I assume what you really want is just this:
array = [
{"id" => "323", "info" => "Test Info"},
{"id" => "324", "info" => "Test Info2"}
]
Given this:
h = {"id" => [323, 324], "info" => ["Test Info", "Test Info2"]}
You can do this:
h.map { |k,l| l.map { |v| [ k,v ] } }.reduce(&:zip).map(&:to_h)
From an array of hashes:
response = [
{"label"=>"cat", "name"=>"kitty", "id"=>189955},
{"label" => "dog", "name"=>"rex", "id" => 550081}
]
is there a way to write something like:
response.name.kitty
to retrieve the hash that contains this value:
{"label"=>"cat", "name"=>"kitty", "id"=>189955}
You can do this -
response.select{|x| x["name"] == "kitty"}.first
Use a Proc
response = [{"label"=>"cat", "name"=>"kitty", "id"=>189955},
{"label" => "dog", "name"=>"rex", "id" => 550081}]
finder = Proc.new {|k, v| response.find {|r| r[k] == v} }
Then
finder.call('name', 'rex')
# => {"label"=>"dog", "name"=>"rex", "id"=>550081}
finder.call('label', 'cat')
# => {"label"=>"cat", "name"=>"kitty", "id"=>189955}
You can also pass multiple conditions if required like this:
response = [
{"label"=>"cat", "name"=>"kitty", "id"=>189955},
{"label"=>"cat", "name"=>"kitty", "id"=>189956},
{"label"=>"cat", "name"=>"kitty", "id"=>189957},
{"label"=>"cat", "name"=>"meow", "id"=>189957},
{"label" => "dog", "name"=>"rex", "id" => 550081},
{"label" => "dog", "name"=>"tommy", "id" => 550082},
{"label" => "dog", "name"=>"rex", "id" => 550083}
]
> response.select{|h| h["label"] == "cat" && h["name"] == "kitty" && h["id"] == 189955}
=> [{"label"=>"cat", "name"=>"kitty", "id"=>189955}] # returns array of selected hashes
> response.find{|h| h["label"] == "cat" && h["name"] == "kitty" && h["id"] == 189955}
=> {"label"=>"cat", "name"=>"kitty", "id"=>189955} # returns first match element
if you're using Rails - just use .pluck
array_of_hashes = [
{ name: "Kate", city: "Minsk", country: "Belarus", id: 1 },
{ name: "Mike", city: "New York", country: "USA", id: 2 },
{ name: "Aleh", city: "Warsaw", country: "Poland", id: 3},
]
>> array_of_hashes.pluck(:name)
>>
>> [ "Kate", "Mike", "Aleh" ]