Map array elements to hash values Ruby - arrays

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

Related

Iterate over an array of hashes and add to the value of specific hash values

If you have an array of hashes such as:
t = [{'pies' => 1}, {'burgers' => 1}, {'chips' => 1}]
what would be an efficient and readable way to add 1 to the value of a hash that has a particular key such as 'pies'?
Here's one way to increment the value(s) of an array's hashes based on a desired key:
t = [{ 'pies' => 1 }, { 'burgers' => 1 }, { 'chips' => 1 }]
t.each { |hash| hash['pies'] += 1 if hash.key?('pies') }
# => [{"pies"=>2}, {"burgers"=>1}, {"chips"=>1}]
Hope this helps!
If you know there's only one hash that could take the key 'pies' then you can use find and increase the value it has, like:
array = [{ 'pies' => 1 }, { 'burgers' => 1 }, { 'chips' => 1 }]
pies_hash = array.find { |hash| hash['pies'] }
pies_hash['pies'] += 1
p array
# [{"pies"=>2}, {"burgers"=>1}, {"chips"=>1}]
Enumerable#find will try to find the element that satisfies the block and stops the iteration when it returns true.
You're using the wrong data structure. I recommend using a Hash.
Each item on your menu can only have one count (or sale), that is each item is unique. This can be modelled with a hash with unique keys (the items) and their corresponding values (the counts).
t = {'pies' => 1, 'burgers' => 1, 'chips' => 1}
Then we can access keys and add to the count:
t['pies'] += 1
t #=> t = {'pies' => 2, 'burgers' => 1, 'chips' => 1}

Looping through an array, displaying elements that match a criteria

I have this big array that I need to break down and only display specific elements within it that match a criteria.
My array looks like this.
[
{
:id => 9789,
:name => "amazing location",
:priority => 1,
:address_id => 12697,
:disabled => false
},
{
:id => 9790,
:name => "better location",
:priority => 1,
:address_id => 12698,
:disabled => false
},
{
:id => 9791,
:name => "ok location",
:priority => 1,
:address_id => 12699,
:disabled => true
}
]
What I need is to only display the elements within this array that have disabled set to true.
However when I try this, I get the error stating no implicit conversion of Symbol into Integer
array.map do |settings, value|
p hash[:disabled][:true]
end
I'm wondering if there is another way, or if there is a way to do this. If anyone could take a look, I would greatly appreciate it.
By providing two arguments to #map on an array, you're actually getting the first hash and then nil. When in reality you just want to loop for each and select those where disabled is true. You can do that instead with Array#select which will filter all elements of the array where the block returns a truthy value:
print array.select { |hash| hash[:disabled] }
=> [{:id=>9791, :name=>"ok location", :priority=>1, :address_id=>12699, :disabled=>true}]
You can try this with a short each or select.
a.each { |k,_v| puts k if k[:disabled] == true }
=> {:id=>9791, :name=>"ok location", :priority=>1, :address_id=>12699, :disabled=>true}
This iterates over each element (hash) inside the array you have and checks if the value of the key disabled on each value is true, and puts the key, just for example, you can set it as you want to do.
Or shorter:
puts a.select { |k,_v| k[:disabled] }
=> {:id=>9791, :name=>"ok location", :priority=>1, :address_id=>12699, :disabled=>true}
Your error shows up when you are treating an array or string as a Hash.
In PHP, array keys can be either numbers or strings, whereas in Ruby associative arrays are a separate data type, called a hash.
Here’s a cheatsheet for various foreach variants, translated into idiomatic Ruby:
Looping over a numeric array (PHP) :
<?php
$items = array( 'orange', 'pear', 'banana' );
# without indexes
foreach ( $items as $item ) {
echo $item;
}
# with indexes
foreach ( $items as $i => $item ) {
echo $i, $item;
}
Looping over an array (Ruby) :
items = ['orange', 'pear', 'banana']
# without indexes
items.each do |item|
puts item
end
# with indexes
items.each_with_index do |item, i|
puts i, item
end
Looping over an associative array (PHP) :
<?php
$continents = array(
'africa' => 'Africa',
'europe' => 'Europe',
'north-america' => 'North America'
);
# without keys
foreach ( $continents as $continent ) {
echo $continent;
}
# with keys
foreach ( $continents as $slug => $title ) {
echo $slug, $title;
}
Looping over a hash (Ruby):
continents = {
'africa' => 'Africa',
'europe' => 'Europe',
'north-america' => 'North America'
}
# without keys
continents.each_value do |continent|
puts continent
end
# with keys
continents.each do |slug, title|
puts slug, title
end
In Ruby 1.9 hashes were improved so that they preserved their internal order. In Ruby 1.8, the order in which you inserted items into a hash would have no correlation to the order in which they were stored, and when you iterated over a hash, the results could appear totally random. Now hashes preserve the order of insertion, which is clearly useful when you are using them for keyword arguments in method definitions. (thanks steenslag for correcting me on this)

dynamically add keys and values to hash and hash within hash

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

Perl sort by hash value in array of hashes or hash of hashes

Can anybody tell me what I am doing wrong here? I have tried just about every possible combination of array / hash type and sort query I can think of and cannot seem to get this to work.
I am trying to sort the hash ref below by value1 :
my $test = {
'1' => { 'value1' => '0.001000', 'value2' => 'red'},
'2' => { 'value1' => '0.005000', 'value2' => 'blue'},
'3' => { 'value1' => '0.002000', 'value2' => 'green'},
'7' => { 'value1' => '0.002243', 'value2' => 'violet'},
'9' => { 'value1' => '0.001005', 'value2' => 'yellow'},
'20' => { 'value1' => '0.0010200', 'value2' => 'purple'}
};
Using this sort loop:
foreach (sort { $test{$a}->{'value1'} <=> $test{$b}->{'value1'} } keys \%{$test} ){
print "key: $_ value: $test->{$_}->{'value1'}\n"
}
I get:
key: 1 value: 0.001000
key: 3 value: 0.002000
key: 7 value: 0.002243
key: 9 value: 0.001005
key: 2 value: 0.005000
key: 20 value: 0.0010200
I have tried with integers and the same thing seems to happen.
I don't actually need to loop through the hash either I just want it ordered for later use. Its easy to do with an array of hashes, but not so with a hash of hashes..?
Don't call keys on a reference. Call it on the actual hash.
Also, this $test{$a}->, should be $test->{$a}, because $test is a hash reference.
foreach (sort { $test->{$a}{'value1'} <=> $test->{$b}{'value1'} } keys %{$test} ){
print "key: $_ value: $test->{$_}->{'value1'}\n"
}
If you had use strict; and use warnings; turned on, you would've gotten the following error to alert you to an issue:
Global symbol "%test" requires explicit package name
Just wanted to provide a source for the other answers, and a working code example. Like they said, you are calling keys with a hash reference for the argument. According to the documentation:
Starting with Perl 5.14, keys can take a scalar EXPR, which must
contain a reference to an unblessed hash or array. The argument will
be dereferenced automatically. This aspect of keys is considered
highly experimental. The exact behaviour may change in a future
version of Perl.
for (keys $hashref) { ... }
for (keys $obj->get_arrayref) { ... }
However this does work for me:
#!/usr/bin/perl
use strict;
use warnings;
my $test = {
'1' => { 'value1' => '0.001000', 'value2' => 'red'},
'2' => { 'value1' => '0.005000', 'value2' => 'blue'},
'3' => { 'value1' => '0.002000', 'value2' => 'green'},
'7' => { 'value1' => '0.002243', 'value2' => 'violet'},
'9' => { 'value1' => '0.001005', 'value2' => 'yellow'},
'20' => { 'value1' => '0.0010200', 'value2' => 'purple'}
};
foreach (sort { $test->{$a}->{'value1'} <=> $test->{$b}->{'value1'} } keys \%{$test} ) {
print "key: $_ value: $test->{$_}->{'value1'}\n"
}
Example:
matt#mattpc:~/Documents/test/10$ perl test.pl
key: 1 value: 0.001000
key: 9 value: 0.001005
key: 20 value: 0.0010200
key: 3 value: 0.002000
key: 7 value: 0.002243
key: 2 value: 0.005000
matt#mattpc:~/Documents/test/10$ perl --version
This is perl 5, version 14, subversion 2 (v5.14.2) built for x86_64-linux-gnu-thread-multi
(with 88 registered patches, see perl -V for more detail)
This is with using a hash reference as the input to keys which I would not recommend.
I'd recommend following the advice of the other questions and adding use strict and use warnings and changing the hash reference to a hash, %{test}.
It's simply keys %$test. The argument of keys must be a hash, not a hashref. \%${test} is the same as $test, a ref.
And use $test->{$a}, not $test{$a}, as $test is a hash-ref, not a hash.
foreach (sort { $test->{$a}->{'value1'} <=> $test->{$b}->'{value1'} } keys %$test) {
print "key: $_ value: $test->{$_}->{'value1'}\n"
}
or a shorter form with some syntactic sugar: You can omit the additional arrows after the first one. And you don't have to quote string literal keys when addressing hashes.
foreach (sort { $test->{$a}{value1} <=> $test->{$b}{value1} } keys %$test) {
print "key: $_ value: $test->{$_}{value1}\n"
}
It usually helps a lot to turn on use warnings;, at least for debugging.
The only wrong thing I can spot is usage of hash ref \%{$test} where you should use hash %$test. keys work with that.

Extracting an array of non=sibling hash values from a nested data structure in perl

This is my data structure created by Data::Dumper->Dumper:
$VAR1 = {
'name' => 'genomic',
'class' => [
{
'reference' => [
{
'name' => 'chromosome',
'referenced-type' => 'Chromosome'
},
{
'name' => 'chromosomeLocation',
'referenced-type' => 'Location'
},
{
'name' => 'sequence',
'referenced-type' => 'Sequence'
},
{
'name' => 'sequenceOntologyTerm',
'referenced-type' => 'SOTerm'
}
],
}
],
};
(trimmed for clarity)
I would like to return a reference to an array of each name value under reference in a single line.
Currently I have
$class->[0]{reference}[0..3]{name}
but no avail.
Also this example has four sibling-hashes with indexes 0..3, how can I represent the whole array independent of the number of elements?
There isn't an easy syntax to do that, unfortunately. You'll have to use map:
my $array_ref = [
map { $_->{name} } #{ $class->[0]{reference} }
];
Then, if you dump out $array_ref, you'll see it contains:
$array_ref = [
'chromosome',
'chromosomeLocation',
'sequence',
'sequenceOntologyTerm'
];
If you need references to the original strings (not copies), you just need a backslash before $_ (so it'd be \$_->{name} inside the map).
$class->[0]{reference} is an array reference, so you have to dereference it with #{}:
#{$class->[0]{reference}}
Is the 'whole array', you can then use slice syntax on the end to get a part of it:
#{$class->[0]{reference}}[0..3]
From there you're working with an array of hashrefs, so you'll have to iterate over it with for or map.

Resources