I'm getting a hash return from our API team that includes an empty key for one of the items. The response looks like this:
user = [
{
'name' : 'John Doe',
'roles' : {
'' : 'admin',
'team2' : 'user'
}
]
I'd like to rename the roles[''] key to something like roles['default'], but I can't find a way to do so. If I try something like this:
user.roles.each {|r| r['default'] = r.delete('')}
it returns Can't convert String into Integer
Is there another way to accomplish this, short of rewriting the API return (it's been made clear that is off the table).
You don't need to call each on roles. That will enumerate all key/value pairs. When you are interested only in one pair. This should do it:
user.roles['default'] = user.roles.delete('')
Edit
user = {
'name' => 'John Doe',
'roles' => {
'' => 'admin',
'team2' => 'user'
}
}
user['roles']['default'] = user['roles'].delete('')
user # => {"name"=>"John Doe", "roles"=>{"team2"=>"user", "default"=>"admin"}}
h = user[0].roles
h.select {|k, v| k == ''}.each { h['default'] = h['']; h.delete('') }
Related
I have a hash that looks like
'Jarrod' => {
'Age' => '25 ',
'Occupation' => Student
},
'Elizabeth' => {
'Age' => '18',
'Occupation' => Student
},
'Nick' => {
'Age' => '32 ',
'Occupation' => Lawyer
},
I am trying to sort them by age so it will look like
'Nick' => {
'Age' => '32 ',
'Occupation' => Lawyer
},
'Jarrod' => {
'Age' => '25 ',
'Occupation' => Student
},
'Elizabeth' => {
'Age' => '18',
'Occupation' => Student
},
But I can't seem to figure out how to access anything past Age. How can I access the value of a value when ordering hash keys?
A hash variable %h with the shown data can be processed in the desired order as
use Data::Dump qw(pp); # to print out a complex data structure
say "$_->[0] => ", pp($_) for
map { [ $_, $h{$_} ] }
sort { $h{$b}->{Age} <=> $h{$a}->{Age} }
keys %h;
what prints (from a complete program below)
Nick => { Age => "32 ", Occupation => "Lawyer" }
Jarrod => { Age => "25 ", Occupation => "Student" }
Elizabeth => { Age => 18, Occupation => "Student" }
Note though that we cannot "sort a hash" and then have it be that way, as hashes are inherently random with order.† But we can of course go through and process the elements in a particular order, as above for example.
Explanation: sort takes pairs of elements of a submitted list in turn, available in variables $a and $b, and then runs the block of code we provide, so to compare them as prescribed. Here we have it compare, and thus sort, the elements by the value at key Age.
The output, though, is just those keys sorted as such! So we then pass that through a map, which combines each key with its hashref value and returns those pairs, each in an arrayref. That is used to print them, as a place holder for the actual processing.
A complete program
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd pp);
my %h = (
'Jarrod' => {
'Age' => '25 ',
'Occupation' => 'Student'
},
'Elizabeth' => {
'Age' => '18',
'Occupation' => 'Student'
},
'Nick' => {
'Age' => '32 ',
'Occupation' => 'Lawyer'
},
);
say "$_->[0] => ", pp($_->[1]) for
map { [ $_, $h{$_} ] } sort { $h{$b}->{Age} <=> $h{$a}->{Age} } keys %h;
Or, for a workable template, change to something like
my #sorted_pairs =
map { [ $_, $h{$_} ] } sort { $h{$b}->{Age} <=> $h{$a}->{Age} } keys %h;
for my $key_data (#sorted_pairs) {
say $key_data->[0], ' => ', pp $key_data->[1]; # or just: dd $key_data;
# my ($name, $data) = #$key_data;
# Process $data (a hashref) for each $name
}
Once we start building more suitable data structures for ordered data then there are various options, including one-person hashrefs for each name, stored in an array in the right order. Ultimately, all that can be very nicely organized in a class.
Note how Sort::Key makes the sorting part considerably less cumbersome
use Sort::Key qw(rnkeysort); # rn... -> Reverse Numerical
my #pairs = map { [ $_, $h{$_} ] } rnkeysort { $h{$_}->{Age} } keys %h;
The more complex -- or specific -- the sorting the more benefit from this module, with its many specific functions for generic criteria.
If these ages are given as integers then there is ikeysort (and rikeysort) and then those are hopefully unsigned integers, for which there is ukeysort (and rukeysort).
† See keys and perlsec, for example.
You can't sort a hash. A hash's elements are inherently unordered.
If you just want to visit the elements of hash in order, you can do that by getting and sorting the keys.
for my $name (
sort { $people{ $b }{ age } <=> $people{ $a }{ age } }
keys( %people )
) {
my $person = $people{ $name };
...
}
or
use Sort::Key qw( rikeysort );
for my $name (
rikeysort { $people{ $_ }{ age } }
keys( %people )
) {
my $person = $people{ $name };
...
}
If you need an ordered structure, you could start by converting the data to an array of people.
my #unordered_people =
map { +{ name => $_, %{ $people{ $_ } } }
keys( %people );
Then sorting that.
my #ordered_people =
sort { $b->{ age } <=> $a->{ age } }
#unordered_people;
or
use Sort::Key qw( rikeysort );
my #ordered_people =
rikeysort { $_->{ age } }
#unordered_people;
I have a ManyToMany relationship setup but need to specify a value for the key in my array to attach.
$data = request()->validate([
'homework_title' => '', // string
'homework_description' => '', // string
'group_id' => '', // array
]);
$homework = request()->user()->homeworks()->create([
'homework_title' => $data['homework_title'],
'homework_description' => $data['homework_description'],
]);
$homework->groups()->attach($data['group_id'][key]);
group_id is an array and looks like the following:
[
{
"group_id": 1,
"group_name": "8x/En2"
},
{
"group_id": 2,
"group_name": "9x/En3"
}
]
How do I specify to attach only the group_id in array?
Got it to work, with:
Arr::pluck()
First time round I forgot to import the class (stupid me)!
use Illuminate\Support\Arr;
finally looking like the following:
$homework->groups()->attach(Arr::pluck($data['group_id'], 'group_id'));
I have a array
array_hash = [
{
"array_value" => 1,
"other_values" => "whatever",
"inner_value" => [
{"iwantthis" => "forFirst"},
{"iwantthis2" => "forFirst2"},
{"iwantthis3" => "forFirst3"}
]
},
{
"array_value" => 2,
"other_values" => "whatever2",
"inner_value" => [
{"iwantthis" => "forSecond"},
{"iwantthis2" => "forSecond2"},
{"iwantthis3" => "forSecond3"}
]
},
]
I want to delete inner value or pop it out (i prefer pop).
So my output should be this:
array_hash = [
{
"array_value" => 1,
"other_values" => "whatever"
},
{
"array_value" => 2,
"other_values" => "whatever2"
},
]
I tried delete_if
array_hash.delete_if{|a| a['inner_value'] }
But it deletes all data in the array. Is there any solution?
try this:
array_hash.map{ |a| {'array_value' => a['array_value'], 'other_values' => a['other_values'] }}
You are telling ruby to delete all hashes that have a key called inner_value. That explains why the array remains empty.
What you should probably do instead is:
array_hash.each { |x| x.delete 'inner_value' }
which means: for each hash in this array, erase the inner_value key.
Well I found it,
array_hash_popped = array_hash.map{ |a| a.delete('inner_value') }
This will pop (because I want pop as stated in the question) the inner_value out and hence inner value will be reduced/deleted from array_hash.
Hi all I am making a request to the Campaign Monitor API and the data has to be held with in single quotes as a string but in JSON format. As it has to be single quotes I am unable to put in any variable values. Below is the code.
response = HTTParty.post(url,
:basic_auth => auth, :body => '{
"EmailAddress":"js#mike.com",
"Name":"#{#fname}",
"CustomFields":[
{
"Key":"firstName",
"Value":"#{#fname}"
},
{
"Key":"country",
"Value":"#{#country}"
}
],
"Resubscribe":true,
"RestartSubscriptionBasedAutoresponders":true
}')
I have tried a few different methods such as breaking the string up and piecing it back together with the variable with in double quotes but that has failed as well for the request to be successful it has to be exact.
Instead of building a JSON structure by hand, you can build a Ruby hash and convert it to JSON:
require 'json'
data = {
'EmailAddress' => 'js#mike.com',
'Name' => #fname,
'CustomFields' => [
{ 'Key' => 'firstName', 'Value' => #fname },
{ 'Key' => 'country', 'Value' => #country }
],
'Resubscribe' => true,
'RestartSubscriptionBasedAutoresponders' => true
}
response = HTTParty.post(url, basic_auth: auth, body: data.to_json)
Also note that there's a Ruby gem for the Campaign Monitor API: createsend-ruby
Using the gem, the above code translates to:
custom_fields = [
{ 'Key' => 'firstName', 'Value' => #fname },
{ 'Key' => 'country', 'Value' => #country }
]
response = CreateSend::Subscriber.add(auth, list_id, 'js#mike.com', #fname, custom_fields, true, true)
you can try with heredoc :
response = HTTParty.post(url,
:basic_auth => auth, :body => <<-BODY_CONTENT
{
"EmailAddress":"js#mike.com",
"Name":"#{#fname}",
"CustomFields":[
{
"Key":"firstName",
"Value":"#{#fname}"
},
{
"Key":"country",
"Value":"#{#country}"
}
],
"Resubscribe":true,
"RestartSubscriptionBasedAutoresponders":true
}
BODY_CONTENT
)
You can use heredoc as explained here
Double-quoting rules are also followed if you put double quotes around
the identifier. However, do not put double quotes around the
terminator.
puts <<"QUIZ"
Student: #{name}
1.\tQuestion: What is 4+5?
\tAnswer: The sum of 4 and 5 is #{4+5}
QUIZ
You can use here documents:
name = 'John'
<<EOS
This is #{name}
EOS
Alternatively you can use flexible quotes, they can handle both ' and " characters:
name = 'John'
%{
This is #{name}
}
Flexible quoting works with %() and %!! as well.
I am unable to come up with a nice way to access a multidimensional Hash with supplied key names in a splat operator - any suggestions?
Example:
I am having a Hash like
{
'key' => 'value',
'some' => {
'other' => {
'key' => 'othervalue'
}
}
}
and a function definition def foo(*args)
I want to return foo('key') value and foo('some','other','key') othervalue. All I can come up with are rather long and ugly for loops with a lot of nil? checks, and am somehow sure I am missing a more ruby-ish way to do this nice and short. Any hints are appreciated.
Update
Using the reply by Patrick below, I came up with
def foo(hash, *args)
keys.reduce(hash, :fetch)
end
which works as I expect it to. Thanks!
In some other languages this is known as get_in, for example in Clojure and Elixir. Here's a functional-ish implementation in Ruby:
class Hash
def get_in(*keys)
keys.reduce(self, :fetch)
end
end
Usage:
h = {
'key' => 'value',
'some' => {
'other' => {
'key' => 'othervalue'
}
}
}
h.get_in 'some'
#=> {
# "other" => {
# "key" => "othervalue"
# }
# }
h.get_in 'some', 'other'
#=> {
# "key" => "othervalue"
# }
h.get_in 'some', 'other', 'key'
#=> "othervalue"