Building a hash from lines input - arrays

Given the lines with a colon as a separator -
1:s11-base:running:/zones/s11-base:3f9d522c:solaris:excl:-:none:<br>
2:s11-template:running:/zones/s11-template:ce57a4db:solaris:excl:-:none:<br>
...
how can I create a hash like
"zoneid" => "1",
"zonename" => "s11-base",
"state" => "running",
next hash
"zoneid" => "2",
"zonename" => "s11-template",
"state" => "running",
and so on.
I want to iterate over all lines, and assign the specific value to the keys from each line.

Anything like this?
input.lines.map do |line|
id, name, state = line.split(":")
{ "zoneid" => id, "zonename" => name, "state" => state }
end

You can use String#split:
str = "1:s11-base:running:/zones/s11-base:3f9d522c:solaris:excl:-:none:<br>"
splitted_str = str.split(":")
# => ["1", "s11-base", "running", "/zones/s11-base", "3f9d522c", "solaris", "excl", "-", "none", "<br>"]
my_hash = {"zoneid": splitted_str[0], "zonename": splitted_str[1], "state" => splitted_str[2]}
# => {:zoneid=>"1", :zonename=>"s11-base", "state"=>"running"}

Related

How to check if an array contains a certain value?

I am working with an array in this form:
"car_documents_attributes"=>{
"1562523330183"=>{
"id"=>"", "filename"=>"tyYYqHeqSFOnqLHEz5lO_rc_tispor12756_6wldwu.pdf", "document_type"=>"contract"
},
"1562523353208"=>{
"id"=>"", "filename"=>"a9P8TyECRiKbI2YdRVZy_rc_tispor12756_bbtzdz.pdf", "document_type"=>"request"
},
"1562523353496"=>{
"id"=>"", "filename"=>"WCM5FHOfSw6yNSUrfPPm_rc_tispor12756_dqu9r2.pdf", "document_type"=>"notes"
},
...
}
I need to find out if in this array is an item where document_type=contract (there can be none, one or multiple ones).
The way I do it is looping through the array item by item, which can be slow if there are tens of items.
Is there a better and faster way to simply check if in the array is an item with document_type = contract?
That's a hash containing more hashes. What you can do is to access to car_documents_attributes, iterate over those hash values and check if any document_type is "contract":
data = {
"car_documents_attributes" => {
"1562523330183" => { "id" => "", "filename" => "tyYYqHeqSFOnqLHEz5lO_rc_tispor12756_6wldwu.pdf", "document_type" => "contract"},
"1562523353208" => { "id" => "", "filename" => "a9P8TyECRiKbI2YdRVZy_rc_tispor12756_bbtzdz.pdf", "document_type" => "request" },
"1562523353496" => { "id" => "", "filename" => "WCM5FHOfSw6yNSUrfPPm_rc_tispor12756_dqu9r2.pdf", "document_type" => "notes" }
}
}
p data['car_documents_attributes'].any? { |_, doc| doc['document_type'] == 'contract' }
# true
Didn't know it was data coming from the params. If so, you do need to permit what's being received or convert the params to an unsafe hash.
Also, you can try using fetch instead [] when trying to get car_documents_attributes, because if that key isn't in data, it'll throw nil, which would throw a NoMethodError:
data.fetch('car_documents_attributes', []).any? { |_, doc| doc['document_type'] == 'contract' }

Ruby pick up a value in hash of array to reformat into a hash

Is there a way I can pick a value in hash of array, and reformat it to be only hash?
Is there any method I can do with it?
Example
[
{
"qset_id" => 1,
"name" => "New1"
},
{
"qset_id" => 2,
"name" => "New2"
}
]
Result
{
1 => {
"name" => "New1"
},
2 => {
"name" => "New2"
}
}
You can basically do arbitary manipulation using reduce function on array or hashes, for example this will get your result
array.reduce({}) do |result, item|
result[item["qset_id"]] = { "name" => item["name"] }
result
end
You can do the same thing with each.with_object do:
array.each.with_object({}) do |item, result|
result[item["qset_id"]] = { "name" => item["name"] }
end
it's basically the same thing but you don't have to make each iteration return the result (called a 'memo object').
You could iterate over the first hash and map it into a second hash:
h1.map{|h| {h['qset_id'] => {'name' => h['name']}} }
# => [{1=>{"name"=>"New1"}}, {2=>{"name"=>"New2"}}]
... but that would return an array. You could pull the elements into a second hash like this:
h2 = {}
h1.each do |h|
h2[h['qset_id']] = {'name' => h['name']}
end
>> h2
=> {1=>{"name"=>"New1"}, 2=>{"name"=>"New2"}}

How to remove hashes from array based on if all keys in hash match another hash

I have an array of hashes where each hash is a list of URL parameters from URI::decode_www_form. I want to remove duplicates in this array so that all hashes inside the array have unique parameter keys.
For example if I have
arr = [{"update" => "1", "reload" => "true"},
{"update" => "5", "reload" => "false"},
{"update" => "9", "reload" => "false"},
{"update" => "7", "reload" => "true", "newvalue" => "11111"},
{"page" => "1"}]
I would expect to have an array containing only:
arr = [{"update" => "1", "reload" => "true"},
{"update" => "7", "reload" => "true", "newvalue" => "11111"},
{"page" => "1"}]
Where the first three entries are duplicates of each other so only keep one of them, the fourth being unique since it has an extra unique key the first three did not have, and the fifth being unique since it is not the same as any of them.
How would I attempt to solve this problem?
You could solve it like this:
tmp = {}
b = arr.select do |h|
if tmp[h.keys]
false
else
tmp[h.keys] = true
true
end
end
arr = [{"update" => "1", "reload" => "true"},
{"update" => "5", "reload" => "false"},
{"update" => "9", "reload" => "false"},
{"update" => "7", "reload" => "true", "newvalue" => "11111"},
{"page" => "1"}]
arr.uniq(&:keys)
#=> [{"update"=>"1", "reload"=>"true"},
# {"update"=>"7", "reload"=>"true", "newvalue"=>"11111"},
# {"page"=>"1"}]
See the doc for Array#uniq for the case where uniq takes a block. In effect, Ruby is doing the following to determine which elements of arr to select:
a = arr.map(&:keys)
#=> [["update", "reload"],
# ["update", "reload"],
# ["update", "reload"],
# ["update", "reload", "newvalue"],
# ["page"]]
a.uniq
#=> [["update", "reload"], ["update", "reload", "newvalue"], ["page"]]
arr.uniq(&:keys) has the same effect as:
arr.uniq { |h| h.keys }
#=> [{"update"=>"1", "reload"=>"true"},
# {"update"=>"7", "reload"=>"true", "newvalue"=>"11111"},
# {"page"=>"1"}]
Many view arr.uniq(&:keys) as just a short-hand way of writing the above expression with the block. That's OK, but actually arr.uniq(&:keys) converts the method (represented by the symbol) :keys to a proc and then calls the proc.

Iterate into an array of hash

I'm trying to loop into an array of hash :
response = [
{
"element" => A,
"group" => {"created" => 13, "code" => "Paris.rb", :"rsvp_limit" => 40},
"name" => "CODELAB",
"venue" => {"id" => 17485302, "place" => "la cordée", "visibility" => "public"}
},
{
"element" => B,
"group" => {"created" => 13, "code" => "Paris.rb", :"rsvp_limit" => 40},
"name" => "PARISRB",
"venue" => {"id" => 17485302, "place" => "la cordée", "visibility" => "public"}
}
]
When I run
response[0]["name"], it returns "CODELAB"
response[1]["name"] returns "PARISRB"
How can I make a loop to have the name of each element of this array of hash ?
I tried :
response.each_with_index do |resp, index|
puts array[index]["name"]
end
This is the error I get in my console :
NoMethodError: undefined method `[]' for nil:NilClass
from (pry):58:in `block in __pry__'
The array here seems to be a typo. You meant:
response.each_with_index do |resp, index|
puts resp["name"]
end
The index is not needed because the resp variable is correctly initialized to contain the hash at each iteration.
So it can be simplified to:
response.each do |resp|
puts resp["name"]
end
A little bit shorter:
puts response.map{|hash| hash['name']}
# CODELAB
# PARISRB

references in perl: hash of array to another array

I have a problem with referencing a hash in an array to another array.
I have an array #result which looks like this:
#result = (
{ "type" => "variable",
"s" => "NGDP",
"variable" => "NGDP" },
{"type" => "subject",
"s" => "USA",
"subject" => "USA",
"variable" => "NGDP" },
{ "type" => "colon",
"s" => ",",
"colon" => "," },
{ "type" => "subject",
"s" => "JPN",
"subject" => "JPN",
"variable" => "NGDP" },
{ "type" => "operator",
"s" => "+",
"operator => "+" },
{"type" => "subject",
"s" => "CHN",
"subject" => "CHN",
"variable" => "NGDP" },
);
I want to divide this array into colons and push elements of the #result array to another array, so i wrote the script:
for ($i = 0; $i <= $#result; $i++) {
if (defined $result[$i]{subject} or $result[$i]{operator} and not defined $result[$i]{colon}) {
push #part_col, \%{$result[$i]};
}
elsif ($i == $#result) {
push #part_col_all, \#part_col;
}
elsif (defined $result[$i]{colon}) {
push #part_col_all, \#part_col;
my #part_col;
}
}
So what I need is that if I print out $part_col_all[0][0]{subject} the result will be "USA",
and for $part_col_all[1][0]{subject} will be "JPN",
and for $part_col_all[1][1]{operator} will be "+" etc.
My result for $part_col_all[0][0]{subject} is "USA"
and for $part_col_all[0][1]{subject} is "JPN" which should be in $part_col_all[1][0]{subject}.
The result for $part_col_all[0][3]{subject} is "CHN", while it should be in $part_col_all[1][2]{subject}.
I'm making an application which is creating graphs from economical data based on a certain economical input. The #result array is my preprocessed input where I know to which country which variable belongs. If I get an input like GDP USA CAN, JPN+CHN I need to split this input to GDP USA CAN and JPN+CHN. That's why I made a condition, if colon is found, push everything in #part_col to the first element of #part_col_all, and then if it's on the end of the input, push JPN+CHN to the second element of #push_col_all.
So #part_col_all should looks like this:
#part_col_all = (
(
{"type" => "subject",
"s" => "USA",
"subject" => "USA",
"variable" => "NGDP" },
{"type" => "subject",
"s" => "CAN",
"subject" => "CAN",
"variable" => "NGDP" },
),
(
{ "type" => "subject",
"s" => "JPN",
"subject" => "JPN",
"variable" => "NGDP" },
{ "type" => "operator",
"s" => "+",
"operator" => "+" },
{"type" => "subject",
"s" => "CHN",
"subject" => "CHN",
"variable" => "NGDP" },
)
);
I dont know what I'm doing wrong. Sorry if there are any basic mistakes, im a beginner. Thanks a lot.
First, you're missing a quote:
{ "type" => "operator",
"s" => "+",
"operator" => "+" },
^ missing
As for printing, you can do the following:
foreach my $part (#part_col){
print $part->{operator}."\n";
}
Or do whatever you want in the print cycle with the values
You should read the Perl Reference Tutorial to help you.
There's no sin in dereferencing to simplify your code:
my #part_col;
my #part_col_all;
for $i ( 0..$#array ) {
my %hash = ${ $result[$i] }; # Make it easy on yourself. Dereference
if ( defined $hash{subject} or defined $hash{operator} and not defined $hash{colon} ) {
push #part_col, \%hash; # or push, #par_col, $result[$i]
}
}
Notice I changed the for from the three part setup you had to a cleaner and easier to understand way of stating it.
Looking closer at your data structure, I notice that $hash{type} will tell you whether or not $hash{operator}, $hash{subject}, or $hash{colon} is defined. Let's just use $hash{type} and simplify that if:
my #part_col;
my #part_col_all;
for my $i ( 0..$#array ) {
my %hash = ${ $result[$i] }; # Make it easy on yourself. Dereference
if ( $hash{type} eq "subject" or $hash{type} eq "operator" ) {
push #part_col, \%hash; # or push, #par_col, $result[$i]
}
}
In fact, since #array is just an array, I'll treat it like one. I'll use a simple for structure to go through each element of my array. Each element is a hash_reference, so:
for my $hash_ref ( #array ) {
my %hash = %{ %hash_ref };
if ( $hash{type} eq "subject" or $hash{type} eq "operator" ) {
push #part_col, \%hash;
}
}
And further simplification, I can dereference and talk about a particular element of my hash all at once by using the -> syntax:
for my $hash_ref ( #array ) {
if ( $hash_ref->{type} eq "subject" or $hash_ref->{type} eq "operator" ) {
push #part_col, $hash_ref;
}
}
I'm trying to understand the rest of your code:
elsif ($i == $#result) {
push #part_col_all, \#part_col;
}
elsif (defined $hash_ref->{colon}) {
push #part_col_all, \#part_col;
my #part_col;
}
}
These pushes of #part_col onto #part_col_all confuse me. Exactly what are you trying to store in #part_col_all? Remember that \#part_col is the location in memory where you're storing #part_col. You're pushing that same memory location over and over onto that hash, so you're storing the same reference over and over again. Is that really what you want? I doubt it.
You need to do is to decide exactly what your data structure really represents. A data structure should have a solid definition. What does the data structure #part_col_all represent? What does the data structure $part_col_all[$i] represent? What does the data structure $part_col_all[$i]->[$j] represent? Without knowing this, it's very hard to answer the rest of your question.
Are you storing elements where the type is colon in one array and everything else in another array? Or are you storing everything in one array, and in another array, storing everything that's not a type colon?
Once I understand this, I can answer the rest of your question.
Addendum
Thank you for your reply, I will try that way and write my results. It is realy helpful. I updated my question with more information about data structure of #part_col_all. I hope that you understand what I'm trying to explain, if not I'll try it again.
If I understand what you're doing, someone enters in NGDP USA , JPN+CNA and that means you're comparing the NGDP between the United States vs. Japan and China combined.
It seems to me that you would want three separate variables:
$parameter - What you are measuring. (GDP, etc.)
#countries_set_1 - The first set of countries
#countries_set_2 - The second set of countries which you're comparing against the first set.
And, what you call the colon (which we would call a comma in the U.S.) as a separator between the first set of countries vs. the second set. Then, you'd simply go through a loop. It could be that the two arrays are merely two elements of the same array, and the sets of countries are array references. I imagine something like this:
#input = qw(GDP USA, JPN CHN); # Compare the GDP of the USA with Japan and China together
my $parameter = shift #input; # Remove what you're measuring
my #country_sets; # An array of arrays
my $set = 0 # Which set you're on
for my $value ( #input ) {
if ( $value eq "," ) {
$set += 1; # Next Set
next;
}
push #{ $country_sets[$set] }, $input;
}
This would create a data structure like this:
#country_sets = (
(
USA,
),
(
JPN,
CHN,
),
)
No need for the complex #results since you're only going to have a single operation (GDP, etc.) for all involved.
However, I think I see what you want. We'll go with an array of arrays. Here's what I had before:
for my $hash_ref ( #array ) {
if ( $hash_ref->{type} eq "subject" or $hash_ref->{type} eq "operator" ) {
push #part_col, $hash_ref;
}
}
We'll combine that and the code I offered right above which splits the countries into two sets:
my #country_sets; # An array of arrays
my $set = 0 # Which set you're on
for my $country_ref ( #array ) {
next if $country_ref->{type} eq "variable"; # We don't want variables
if ( $country_ref{type} eq "colon" ) { # Switch to the other country set
set += 1;
next;
}
push #{ $country_sets[$set] }, $country_ref;
}
The first few entries will go into $country_sets[0] which will be an array reference. After the colon (which won't be input into the set), the second set of countries will go into $country_sets[1] which will be an other array_ref to a reference of hashes:
#country_sets - Contains the input information into two sets
#country_sets[$x] - A particular set of countries (and possibly operator)
#country_sets[$x]->[$y] - A Particular country or operator
#country_sets[$x]->[$y]->{$key} - A particular value from a particular country
Where $x goes from 0 to 1. This will give you something like this:
$country_sets[0] = (
{
"type" => "subject",
"s" => "USA",
"subject" => "USA",
"variable" => "NGDP",
},
)
$country_sets[1] = (
{
"type" => "subject",
"s" => "JPN",
"subject" => "JPN",
"variable" => "NGDP",
},
{
"type" => "operator",
"s" => "+",
"operator => "+",
},
{
"type" => "subject",
"s" => "CHN",
"subject" => "CHN",
"variable" => "NGDP",
},
);

Resources