"Display information for each person" - ruby

I can't get the code to display the information about the people correctly. I'm able to list the names but not the ages.
my_group = ["person_1", "person_2", "person_3"]
person_1 = {name: "erik", gender: "male", age: 26}
person_2 = {name: "erika", gender: "female", age: 26}
person_3 = {name: "erka", gender: "alpha", age: 27}
my_group.each do |name|
my_group.each do |age|
puts "Hello, #{name} is about #{age} years old"
end
puts "Hello, #{name} is about #{age} years old"
end
I don't get an error code, but it does not list the age with the names.

Actually your my_group contains only Strings. If you want to include persons variable you may include them as variables (without ""):
person_1 = {name: "erik", gender: "male", age: 26}
person_2 = {name: "erika", gender: "female", age: 26}
person_3 = {name: "erka", gender: "alpha", age: 27}
my_group = [ person_1, person_2, person_3 ]
Then you can use Array#each method to loop on each person into this array.
my_group.each do |person|
puts "Hello, #{person[:name]} is about #{person[:age]} years old"
end
Also note that each person is a Hash. If you want to access to age property of person_1, for example, you have to do this:
puts person_1[:name]

Related

adding key values inside of an array of hashes using .each

I'm wondering what the easiest method for adding 'string' values in an array of hashes using an each statement?
lost_boys = [
{name: 'Tootles', age: '11'},
{name: 'Nibs', age: '9'},
{name: 'Slightly', age: '10'},
{name: 'Curly', age: '8'},
{name: 'The Twins', age: '9'}
]
a = []
lost_boys.each do |ages|
a = ages[:age].to_i
puts a
end

Using new Ruby pattern matching to check if a hash has certain keys

I want to use the new Ruby 3 feature in this very simple case. I know it must be possible but I have not figured it out from the documentation.
Given a hash, I want to check that it has certain keys. I don't mind if it has others in addition. And I want to do this with pattern matching (or know that it is impossible.) I also don't want to use a case statement which seems overkill.
{name: "John", salary: 12000, email: "john#email.com" }
Raise an error if the hash does not have name, and email as strings and salary as a number.
Use the contruct in an if or other conditional?
And what if the hash has strings as keys (which is what I get from JSON.parse) ?
{"name" => "John", "salary" => 12000, "email" => "john#email.com" }
You're looking for the => operator:
h = {name: "John", salary: 12000, email: "john#email.com" }
h => {name: String, salary: Numeric, email: String} # => nil
With an additional pair (test: 0):
h[:test] = 0
h => {name: String, salary: Numeric, email: String} # => nil
Without the :name key:
h.delete :name
h => {name: String, salary: Numeric, email: String} # key not found: :name (NoMatchingPatternKeyError)
With the :name key but the class of its value shouldn't match:
h[:name] = 1
h => {name: String, salary: Numeric, email: String} # String === 1 does not return true (NoMatchingPatternKeyError)
A strict match:
h[:name] = "John"
h => {name: String, salary: Numeric, email: String} # => rest of {:test=>0} is not empty
The in operator returns a boolean value instead of raising an exception:
h = {name: "John", salary: 12000, email: "john#email.com" }
h in {name: String, salary: Numeric, email: String} # => true
h[:name] = 1
h in {name: String, salary: Numeric, email: String} # => false
"I also don't want to use a case statement which seems overkill." case is just the syntax for pattern matching. AFAIK it is not the same as a case when, it's a case in.
h = {name: "John", salary: 12000, email: "john#email.com", other_stuff: [1] }
case h
in {name: String, salary: Integer, email: String}
puts "matched"
else
raise "#{h} not matched"
end

How can ONE Ruby-symbol map to MULTIPLE values?

From what I have read so far I know, that a symbol is pointing always to the same value. Unlike a string, which is mutable in Ruby. Means a string might not have the same value all the time.
But if I create a list which hashes like e.g. this one:
persons = [
{first_name: "John", last_name: "Doe"},
{first_name: "Lisa", last_name: "Meyer"}
]
If I now do:
persons[1][:first_name]
Then I get the correct value "Lisa".
But how does that work?
If a symbol can point to only one exact value:
How can it differentiate between the "first_name"-symbol of the first hash and the "first_name"-symbol of the second, third ... hash?
persons = [
{ first_name: "John", last_name: "Doe" },
{ first_name: "Lisa", last_name: "Meyer"}
]
persons is an array of hashes. Arrays can be accessed by their indexes.
So, the index 0 in persons is { first_name: "John", last_name: "Doe" }.
The index 1 in persons is { first_name: "Lisa", last_name: "Meyer"} and so on:
p persons[0] # {:first_name=>"John", :last_name=>"Doe"}
p persons[1] # {:first_name=>"Lisa", :last_name=>"Meyer"}
Don't get confused in mutability here. It's just you're referring to the wrong data type.
If you want, you can check every hash key from persons and see that they have the same object_id (first_name and last_name). That's because symbols are immutables, allowing Ruby to create only one instance of them as long as your code is running:
persons.map { |e| e.map { |key, _| [key, key.object_id] }.to_h }
# [{:first_name=>1016988, :last_name=>1017308}, {:first_name=>1016988, :last_name=>1017308}]
As per the comment question; no, it doesn't create a new object depending on the scope, e.g.:
p :first_name.object_id
# 1016668
def persons
[{ first_name: "John", last_name: "Doe" },
{ first_name: "Lisa", last_name: "Meyer"}]
end
p persons.map { |e| e.map { |key, _| [key, key.object_id] }.to_h }
# [{:first_name=>1016668, :last_name=>1017308}, {:first_name=>1016668, :last_name=>1017308}]
:first_name.object_id is defined outside the scope of the persons method, but while inside the method, it keeps pointing to the same object.
In your example you select the object at index 1 of your array.
{first_name: "Lisa", last_name: "Meyer"}
How could the value of the first_name key be different than 'Lisa'?

Removing hashes that have identical values for particular keys

I have an Array of Hashes with the same keys, storing people's data.
I want to remove the hashes that have the same values for the keys :name and :surname. The rest of the values can differ, so calling uniq! on array won't work.
Is there a simple solution for this?
You can pass a block to uniq or uniq!, the value returned by the block is used to compare two entries for equality:
irb> people = [{name: 'foo', surname: 'bar', age: 10},
{name: 'foo', surname: 'bar' age: 11}]
irb> people.uniq { |p| [p[:name], p[:surname]] }
=> [{:name=>"foo", :surname=>"bar", :age=>10}]
arr=[{name: 'john', surname: 'smith', phone:123456789},
{name: 'thomas', surname: 'hardy', phone: 671234992},
{name: 'john', surname: 'smith', phone: 666777888}]
# [{:name=>"john", :surname=>"smith", :phone=>123456789},
# {:name=>"thomas", :surname=>"hardy", :phone=>671234992},
# {:name=>"john", :surname=>"smith", :phone=>666777888}]
arr.uniq {|h| [h[:name], h[:surname]]}
# [{:name=>"john", :surname=>"smith", :phone=>123456789},
# {:name=>"thomas", :surname=>"hardy", :phone=>671234992}]
unique_people = {}
person_array.each do |person|
unique_people["#{person[:name]} #{person[:surname]}"] = person
end
array_of_unique_people = unique_people.values
This should do the trick.
a.delete_if do |h|
a.select{|i| i[:name] == h[:name] and i[:surname] == h[:surname] }.count > 1
end

How to sort array of hashes by value?

I'm trying to sort an array of hashes based on the given key-value, and will return that value on top of the array first, then preceded by the remaining data.
Example would be:
students = [{name: "John Doe", age: 16, adviser: "Mrs. Robinson"},
{name: "John Smith", age: 18, adviser: "Mrs. Williams"},
{name: "Michael Rodriguez", age: 17, adviser: "Mr. Lee"}]
def sort_by_adviser(data, name)
...
end
> sort_by_adviser(students, "Mr. Lee")
=> [{name: "Michael Rodriguez", age: 17, adviser: "Mr. Lee"},
{name: "John Doe", age: 16, adviser: "Mrs. Robinson"},
{name: "John Smith", age: 18, adviser: "Mrs. Williams"}]
> sort_by_adviser(students, "Mrs. Williams")
=> [{name: "John Smith", age: 18, adviser: "Mrs. Williams"},
{name: "Michael Rodriguez", age: 17, adviser: "Mr. Lee"},
{name: "John Doe", age: 16, adviser: "Mrs. Robinson"}]
Here the output brings the adviser's name on top of the list, then preceded by other hashes in the array.
> sort_by_keyvalue(data, "Z")
=> [{letter: 'Z'},
{letter: 'A'},
.
.
.
{letter: 'Y'}]
> sort_by_keyvalue(data, 5)
=> [{number: 5, value: 'value1'},
{number: 5, value: 'value2'},
{number: 5, value: 'value3'},
{number: 9, value: 'value1'},
{number: 9, value: 'value2'},
{number: 8, value: 'value1'},
{number: 8, value: 'value2'},
{number: 7, value: 'value1'},
{number: 6, value: 'value1'},
{number: 4, value: 'value1'},
{number: 3, value: 'value1'},
{number: 2, value: 'value1'},
{number: 1, value: 'value1'},
{number: 1, value: 'value2'},
{number: 0, value: 'value1'}]
Anyone knows how to do it?
Another implemention :)
def sort_by_adviser(data, name)
data.each_with_index do |hash,index|
if hash[:adviser]==name
data.delete_at index #delete from array
data.unshift hash
break
end
end
data
end
> sort_by_adviser(students, "Mr. Lee")
#=> [{:name=>"Michael Rodriguez", :age=>17, :adviser=>"Mr. Lee"}, {:name=>"John Doe", :age=>16, :adviser=>"Mrs. Robinson"}, {:name=>"John Smith", :age=>18, :adviser=>"Mrs. Williams"}]
def creamy_sort(key, value, arr)
top, bottom = arr.partition{|e| e[key] == value }
top.concat(bottom.sort{|a,b| b[key] <=> a[key]})
end
creamy_sort(:adviser, "Mr. Lee", students)
You can do that:
def sort_by_adviser(data, name)
data = data.sort{|x,y|x[:adviser] <=> y[:adviser]}
i = data.index{|h|h[:adviser] = name}
h = data.delete_at i
data.unshift h
end
I have this solution:
students = [{name: "John Doe", age: 16, adviser: "Mrs. Robinson"},
{name: "John Smith", age: 18, adviser: "Mrs. Williams"},
{name: "Michael Rodriguez", age: 17, adviser: "Mr. Lee"}]
def sort_by_adviser(data, *name)
data.sort_by{| entry |
[
name.index(entry[:adviser]) || 999,
entry[:age], entry[:name] #2nd sort criteria
]
}
end
p sort_by_adviser(students, "Mr. Lee")
#[{:name=>"Michael Rodriguez", :age=>17, :adviser=>"Mr. Lee"}, {:name=>"John Doe", :age=>16, :adviser=>"Mrs. Robinson"}, {:name=>"John Smith", :age=>18, :adviser=>"Mrs. Williams"}]
p sort_by_adviser(students, "Mrs. Williams")
# [{:name=>"John Smith", :age=>18, :adviser=>"Mrs. Williams"}, {:name=>"John Doe", :age=>16, :adviser=>"Mrs. Robinson"}, {:name=>"Michael Rodriguez", :age=>17, :adviser=>"Mr. Lee"}]
I didn't understand, what's the sorting of the remaining entry.
You wrote: then preceded by the remaining data. What's tho order criteria of the hash?
I selected age, followed by name. But you may adapt it for your need.
def weird_sort(array, key, value)
return array.sort_by{|d| 2 <=> (d[key] == value).object_id}
end
This is based on the fact that true.object_id equals 2 in ruby.
Kind of a weird solution that's why it's a weird_sort :p It also messes around the other value ordering ... so it only guarantees you that values that are equal go on top!

Resources