I have an array of names
department = ['name1', 'name2', 'name3']
and an array of months
month = ['jan', 'feb', 'mar', 'etc']
I need to dynamically merge these arrays into hashes to look like this:
h = {'name1' => {'jan' => '', 'feb' => '', 'mar' => '', 'etc' => ''},
'name2' => {'jan' => '', 'feb' => '', 'mar' => '', 'etc' => ''}, 'name3' => {'jan' => '', 'feb' => '', 'mar' => '', 'etc' => ''}}
How would I go about dynamically adding keys into my hash?
Here is a way:
department
.each_with_object({}) do |name, h|
# taking an empty hash which will be holding your final output.
h[name] = month.product([""]).to_h
end
Read Array#product to know how month.product([""]) line is working. You can convert an array of array to hash using to_h.
As is often the case the answer lies in the power of Enumerable:
Hash[
department.map do |d|
[
d,
Hash[
month.map do |m|
[ m, '' ]
end
]
]
end
]
There's a lot going on in here, but it boils down to a two part process, one converting the department list into a hash of hashes, and a second part to populate that with a converted month structure.
Hash[] is a handy way of converting key/value pairs into a proper Hash structure.
department = ['name1', 'name2', 'name3']
month = ['jan', 'feb', 'mar', 'etc']
month_hash = month.each_with_object({}) { |m, res| res[m] = '' }
result = department.each_with_object({}) { |name, res| res[name] = month_hash.dup }
p result
This way you could build month_hash, and then use it to build result hash, that you need
Here's how I would do it.
1) Make a Hash out of your months array
month_hash = Hash[month.map { |m| [m ,''] }]
2) Make a Hash out of your department array, inserting the newly made month hash.
result_hash = Hash[department.map { |d| [d, month_hash] }]
month = ['jan', 'feb', 'mar', 'etc']
department = ['name1', 'name2', 'name3']
month_hash = month.map {|m| [m, '']}.to_h
p department.map {|d| [d, month_hash]}.to_h
Related
I am searching for a while now, how I could achieve to turn objects arrays into an array with only values. For example, I have an array with several Restaurants and within these restaurants there is a key named category. Category could have multiple values like Sushi, Chinese, asian. I would like to go trough all object and reduce my array from:
[{
id: '1',
title: 'Italian Dream',
category: 'Pizza, Pasta, Snack',
opening_hours: '08:00-24:00',
},
{
id: '2',
title: 'Turkish Man',
category: 'Döner, Pizza, Lahmacun',
opening_hours: '08:00-24:00',
}]
to
[ Pasta, Snack, Döner, Pizza, Lahmacun]
Would be glad if anybody could give me any advice.
Cheers
Since category is a string and not an array of strings, we need to .split(', ')
Since we have multiple categories per data item, we can use .flatMap to "combine" or flatten that what would have been an array of arrays
We can use new Set(...) to get a unique list of string values
And finally use Array.from(...) to convert the Set to an Array.
const data = [{
id: '1',
title: 'Italian Dream',
category: 'Pizza, Pasta, Snack',
opening_hours: '08:00-24:00',
},
{
id: '2',
title: 'Turkish Man',
category: 'Döner, Pizza, Lahmacun',
opening_hours: '08:00-24:00',
}
]
const categories = Array.from(new Set(data.flatMap(x => x.category.split(', '))))
console.log(categories)
You can loop your array and use split function to extract categories from your string.
let newArray = [];
oldArray.forEach(restaurant => {
newArray.push(...restaurant.category.split(', '));
});
// Here newArray contains categories with duplicate values
// You can use Set to avoid duplicate values.
newArray = [...new Set(newArray)];
I have the following array which is actually a combination of two arrays. My goal is that the 2 hashes with employeeId of 898989 could be combined and that I could add their counts together and change their type to both. I tried the code below which is close to what I want, however I lose the other values of my hashes. Is there any easy way to map all of the values and do manipulation like adding the counts like I want?
combined = [{"#rid"=>"#-2:1", "employeeId"=> "898989", "count"=>1, :type=>"wiki" },
{"#rid"=>"#-2:3", "employeeId"=> "2423213", "count"=>7, :type=>"search"},
{"#rid"=>"#-2:2", "employeeId"=> "555555", "count"=>2, :type=>"search"},
{"#rid"=>"#-2:5", "employeeId"=> "898989", "count"=>2, :type=>"search"},
{"#rid"=>"#-2:1", "employeeId"=> "5453454", "count"=>1, :type=>"search"},
{"#rid"=>"#-2:4", "employeeId"=>"987654321", "count"=>1, :type=>"search"}]
merged_array_hash = combined.group_by { |h1| h1["employeeId"] }.map do |k,v|
{ "employeeId" => k, :types => v.map { |h2| h2[:type] }.join(", ") }
end
merged_array_hash ends up being:
[{employeeId: "898989",types: "wiki, search"},
{employeeId: "2423213",types: "search"},
{employeeId: "555555",types: "search"},
{employeeId: "5453454",types:"search"},
{employeeId: "987654321",types: "search"}]
What I'm looking to get:
[{employeeId: "898989",type: "both", count: 2},
{employeeId: "2423213",type: "search", count: 7},
{employeeId: "555555",type: "search", count: 2},
{employeeId: "5453454",type:"search", count: 1},
{employeeId: "987654321",type: "search", count: 1}]
Not beautiful, but it will get the job done:
combined.group_by { |h1| h1["employeeId"] }.map do |k,v|
types = v.map { |h2| h2[:type] }
count = v.sum { |x| x['count'] }
{ employeeId: k,
types: (types.length == 1 ? types[0] : 'both'),
count: count }
end
=> [{:employeeId =>"898989", :types=>"both", :count=>3},
{:employeeId =>"2423213", :types=>"search", :count=>7},
{:employeeId =>"555555", :types=>"search", :count=>2},
{:employeeId =>"5453454", :types=>"search", :count=>1},
{:employeeId =>"987654321", :types=>"search", :count=>1}]
Also not beautiful, will also get the job done, potentially more readable
hash = {}
combined.each do |h|
employeeId, count, type = h.values_at("employeeId", "count", :type)
if hash.include? employeeId
hash[employeeId][:count] += count
hash[employeeId][:type] = "both" #assumes duplicates can only occur if item is in both lists
else
hash[employeeId] = { :employeeId => employeeId, :type => type, :count => count }
end
end
hash.values
Testing:
[{:employeeId=>"898989", :type=>"both", :count=>3},
{:employeeId=>"2423213", :type=>"search", :count=>7},
{:employeeId=>"555555", :type=>"search", :count=>2},
{:employeeId=>"5453454", :type=>"search", :count=>1},
{:employeeId=>"987654321", :type=>"search", :count=>1}]
I have a 2D array with each row like:
['John', 'M', '34']
I want to map into an array of Hash with each hash like:
{:Name=>"John", :Gender=>"M", :Age=>"34"}
Is there an elegant way of doing that?
array_of_rows.map { |n,g,a| { Name: n, Gender: g, Age: a } }
or
array_of_rows.map { |row| %i{Name Gender Age}.zip(row).to_h }
They produce the same result, so pick the one you find clearer. For example, given this input:
array_of_rows = [
['John', 'M', '34'],
['Mark', 'M', '49']
]
either expression will yield this output:
[{:Name=>"John", :Gender=>"M", :Age=>"34"},
{:Name=>"Mark", :Gender=>"M", :Age=>"49"}]
You could try using zip and then to_h (which stands for to hash)
For example:
[:Name, :Gender, :Age].zip(['John', 'M', '34']).to_h
=> {:Name=>"John", :Gender=>"M", :Age=>"34"}
Read more about zip here
And read about to_h here
people = [['John', 'M', '34']]
keys = %i{Name Gender Age}
hashes = people.map { |person| keys.zip(person).to_h }
# => [{:Name=>"John", :Gender=>"M", :Age=>"34"}]
Basically the way I turn combine two arrays into a hash (one with keys, one with values) is to use Array#zip. This can turn [1,2,3] and [4,5,6] into [[1,4], [2,5], [3,6]]
This structure can be easily turned into a hash via to_h
array_of_rows = [
['John', 'M', '34'],
['Mark', 'M', '49']
]
keys = ['Name', 'Gender', 'Age']
[keys].product(array_of_rows).map { |k,v| k.zip(v).to_h }
#=> [{"Name"=>"John", "Gender"=>"M", "Age"=>"34"},
# {"Name"=>"Mark", "Gender"=>"M", "Age"=>"49"}]
or
keys_cycle = keys.cycle
array_of_rows.map do |values|
values.each_with_object({}) { |value, h| h[keys_cycle.next]=value }
do
Here is one more way to do this
array_of_rows = [
['John', 'M', '34'],
['Mark', 'M', '49']
]
keys = [:Name, :Gender, :Age]
array_of_rows.collect { |a| Hash[ [keys, a].transpose] }
#=>[{:Name=>"John", :Gender=>"M", :Age=>"34"}, {:Name=>"Mark", :Gender=>"M", :Age=>"49"}]
Is it possible to get the intersection of an array and just the keys of a hash?
I know that the & operator returns the intersection of two arrays, but I'd like to use the values instead of the value + key combination.
Let's say, I have an array and a hash :
a1 = [ 'test1', 'test2', 'test3' ]
a2 = { 'test3' => 'value3', 'test4'=>'value4', 'test5'=>'value5' }
is there a way to return value3 from these?
given
arr = ['value1', 'value2', 'value3']
hsh = {:key1 => 'value3', :key2=>'value4', :key3=>'value5'}
you can convert hsh to an array of values with the .values method and use the & operator to compare the hash values with an array.
arr & hsh.values
=> ["value3"]
This is probably the most intuitive way I can think of to do what you're asking:
a1 = [ 'test1', 'test2', 'test3' ]
a2 = { 'test3' => 'value3', 'test4'=>'value4', 'test5'=>'value5' }
(a1 & a2.keys).map { |e| a2[e] }
#=> ["value3"]
That is you want the intersection of a1 and the keys from a2. You can then use map to perform the lookup on each key found in the intersection to return the value.
a1.reduce(nil) {|r, k| r || a2[k]} should do what you're asking.
I have a hash here,
#property_hash = {
:code => '',
:fname => '',
:lname => '',
:basic_sal => '',
:emp_type => '',
}
and an array
line = [02,'Firstname', 'LastName', 5800, 'PL']
I want to map the array into the hash like
#property_hash = {
:code => 02,
:fname => 'Firstname',
:lname => 'LastName',
:basic_sal => 5800,
:emp_type => 'PL',
}
what is the best way to do this ??
Thank you
you can try like this:
#property_hash.each_with_index {|(k, v), index| #property_hash[k] = line[index]}
Not best way but that will work
My solution assumes that line has the same order every time. So I define another array with the field names, merge the corresponding array elements together and convert the result into a hash.
line = [02, 'Firstname', 'LastName', 5800, 'PL']
fields = #property_hash.keys
# => [:code, :fname, :lname, :basic_sal, :emp_type]
key_value_pairs = fields.zip(line)
# => [[:code, 2], [:fname, "Firstname"], [:lname, "LastName"], [:basic_sal, 5800], [:emp_type, "PL"]]
#property_hash = Hash[key_value_pairs]
# => {:code=>2, :fname=>"Firstname", :lname=>"LastName", :basic_sal=>5800, :emp_type=>"PL"}
Memory-wise, it is more efficient to change #property_hash in place rather than setting #property_hash equal to a newly-constructed hash. Here is one way to that:
lc = line.dup
#property_hash.update(#property_hash) { lc.shift }
#=> { :code => 02,
:fname => 'Firstname',
:lname => 'LastName',
:basic_sal => 5800,
:emp_type => 'PL' }
This uses the form of Hash#update (aka merge!) that uses a block to determine the value of keys that are present in both hashes being merged, which here is all of the keys.
Here is one more way it can be done:
[#property_hash.keys, line].transpose.to_h