How To Optimize Method Merging Two Hashes [RUBY] - arrays

I have constructed a method that merges two hashes where one of their keys values are the same. The method can be seen below:
def self.hash_merge_on_location(base, additions_arr, location)
base.each_with_index { |b_hash,b_index|
additions_arr.each do |addition|
addition.each_with_index { |a_hash,a_index|
if b_hash[location] == a_hash[location]
base[b_index] = b_hash.merge(a_hash)
end
}
end
}
base
end
So my question is how can I make this method better? Right now I have base (array of hashes), additions_arr (array of array of hashes), and location (key to compare) as my parameters and I iterate over over hash in my base array and then compare this against every array of hashes in my additions_arr. I finally check to see if the current hash value in my array in my additions_arr is equal to the hash value in my base array.
An example would be as follows:
Base Array
[{"loan_number" => 10808, "spec_type" => "New"},
{"loan_number" => 10809, "spec_type" => "Old"}]
Additions Array
[
[{"loan_number" => 10808, "new_field" => 10},
{"loan_number" => 12383, "new_field" => 19}],
[{"loan_number" => 10809, "new_field" => 11}]
]
So right now with my current code with a location input of "loan_number" the output of hash_merge_on_location would be:
Output
[{"loan_number" => 10808, "spec_type" => "New", "new_field" => 10},
{"loan_number" => 10809, "spec_type" => "Old", "new_field" => 11}]
Is there anyway in which I can optimize my method as it stands currently? Or anything else I could do to make it better?

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)

Sorting an array of hashes, based on the value of a particular key within each hash in perl

I've done some searching and struggled to find the answer to this one.
Say I have an array of hashes like this..
my #AoH = (
{ 'ip_type' => 1,
'ip_ref' => 0001,
'ip_address' => '192.168.0.1',
'ip_priority' => '100'
},
{ 'ip_type' => 1,
'ip_ref' => 0002,
'ip_address' => '192.168.0.2',
'ip_priority' => '1'
}
);
In context, these are multiple IP addresses, in which I intend to load balance across. The 'ip_priority' value dictates which is first, and subsequently which is second in pirority. primary and backup, basically.
However, I want to order the array elements (aka the hashes) numerically based on the ip priority value within the hash.
So, preferably the above AoH would be...
my #AoH = (
{ 'ip_type' => 1,
'ip_ref' => 0002,
'ip_address' => '192.168.0.2',
'ip_priority' => '1'
},
{ 'ip_type' => 1,
'ip_ref' => 0001,
'ip_address' => '192.168.0.1',
'ip_priority' => '100'
}
);
so $AoH[0]->{ip_priority} now cotains 1 and $AoH[1]->{ip_priority} contains 100.
How can I perform a sort in order to achieve the second example above? im struggling to find a workable example!
You can use:
my #sorted = sort {$a->{ip_priority} <=> $b->{ip_priority}} #AoH;

Perl: Is is possible for a Hash to store mixed variables?

Quick question:
I would like to store the below variables in a Hash:
my %hash1;
my %hash2;
my %hash3;
my #array1;
my #array2;
my #array3;
End result being:
Hash
hash1Name
{key1=>val1,key2=>val2}
hash2Name
{key1=>val1,key2=>val2}
hash3Name
{key1=>val1,key2=>val2}
array1Name
[element1,element2,...]
array2Name
[element1,element2,...]
array3Name
[element1,element2,...]
Why do i want to do this:
Well this is all done in a function (sub) so a want to return all data in a single hash.
Is this possible?
Would it be better to just create a class, which can hold these variables?
Thanks
You can store any scalar inside a hash or an array. Scalars include references to arrays or hashes:
my $hash = {
hash1Name => \%hash1,
hash2Name => \%hash2,
hash3Name => \%hash3,
array1Name => \#array1,
array2Name => \#array2,
array3Name => \#array3,
}
Perl has no type system that would get in your way if you want to do this. But you, the programmer, are now responsible for correctly handling the contents of the hash, regardless of the type. If it enables a cleaner design, you can use OO. But be aware that
The underlying object would usually be implemented by such a hash anyway
OO for OO's sake unneccessarily complicates code, when basic data structures are enough.
Yes, it is possible, and most easily done with lexical variables:
sub mysub {
my %hash1;
my %hash2;
my %hash3;
my #array1;
my #array2;
my #array3;
...
my %all = (
hash1 => \%hash1,
hash2 => \%hash2,
array1 => \#array1,
# etc
);
return \%all;
}
You return a single scalar value which is a hash reference, containing all the data.
my %Hash = (
hash1Name => \%hash1,
hash2Name => \%hash2,
hash3Name => \%hash3,
array1Name => \#array1,
array2Name => \#array2,
array3Name => \#array3,
);
But you probably want a reference to a hash:
my $Hash = {
hash1Name => \%hash1,
hash2Name => \%hash2,
hash3Name => \%hash3,
array1Name => \#array1,
array2Name => \#array2,
array3Name => \#array3,
};

How to declare AoHoAoH?

I have this output from Dumper
'group' => {
'1104' => {
'a' => 1
},
'52202' => {
'b' => 1,
'c' => 1
},
'52201' => {
'c' => 1
},
'52200' => {
'c' => 1
}
},
which I assume is an Array of Hashes of Arrays of Hashes?
I would like to declare this structure my self.
Is there a way to do this, so next time I see such a complex structure, I can do that in no time? =)
Your output is a hash of hashes of hashes, with the first hash only containing a single element. The {} mark a hash reference, so you'd repeat your data structure thus, where the resulting $hohoh is a refrence to a HoHoH.
my $hohoh = {
'group' => {
'1104' => {
'a' => 1
},
'52202' => {
'b' => 1,
'c' => 1
},
'52201' => {
'c' => 1
},
'52200' => {
'c' => 1
}
},
};
print $hohoh->{group}{1104}{a}; # -> 1
I recommend reading the Perl Datastructures Cookbook.
Since the types of variables, and of hash values, can change in Perl, there isn't any way to "declare" a three-level hash the way you're probably thinking. You can instantiate an empty hashref into each key as it's created, which is a similar idea:
# First pass
my $data = {};
# Later...
$data->{group} = {};
# Still later...
$data->{group}->{1104} = {};
# Finally...
$data->{group}->{1104}->{a} = 1;
But you could just as easily simply fill in the data as you obtain it, allowing autovivification to do its thing:
my $data;
# Fill one piece of data... Perl creates all three hash levels now.
$data->{group}->{1104}->{a} = 1;
# Fill another piece of data, this one has two values in the "bottom" hash.
$data->{group}->{52202} = { b => 1, c => 2};
But there is no way (in plain Perl) to "enforce" that the values for any particular key contain hashes rather than strings or subroutine references, which is usually what is intended by declaration in languages with C-like type systems.

Resources