Ruby inject condition - ruby

I have an array filled with hashes. The data structure looks like this:
students = [
{
"first_name" => "James",
"last_name" => "Sullivan",
"age" => 20,
"study_results" => {"CAR" => 1, "PR1" => 1, "MA1" => 1, "BEN" => 2, "SDP" => nil}
}
]
I want to find students with the mark 1 from at least two subjects.
I tried to convert the hash with marks into an array and then use the inject method to count the number of 1 and find out if the number is > 1:
students.select{|student
(
(student["study_results"].to_a)
.inject(0){|sum, x| sum += 1 if x.include?(1)}
) > 1
}
Is there any way to put a condition into the method, or should I find a different way to solve it?

I commend you for attempting to solve this before posting, but you made it unnecessarily complicated. I'd write it like this:
students.select{|student| student['study_results'].values.count(1) >= 2}
That's all, no need for inject. You were misusing it here.
Ruby collections have TONS of useful methods. If you find yourself using inject or each, there's a better method for this, 90% of the time.
Explanation:
student['study_results'] # => {"CAR"=>1, "PR1"=>1, "MA1"=>1, "BEN"=>2, "SDP"=>nil}
student['study_results'].values # => [1, 1, 1, 2, nil]
student['study_results'].values.count(1) # => 3
student['study_results'].values.count(2) # => 1
student['study_results'].values.count(3) # => 0
student['study_results'].values.count(nil) # => 1
Documentation
Hash#values
Array#count

Related

How to sort an array of hashes with multiple values?

I have an array of hashes which contains an id field and a weight field. I am able to sort it based on weight but I also need to make sure that the id field is sorted if there are duplicate weights. Below is the code snippet for reference.
# Input
arr = [{"id" => 10, "weight" => 23}, {"id" => 6, "weight" => 43}, {"id" => 12, "weight" => 5}, {"id" => 15, "weight" => 30}, {"id" => 11, "weight" => 5}]
arr.sort_by{|k| k["weight"]}
# Output: [{"id"=>12, "weight"=>5}, {"id"=>11, "weight"=>5}, {"id"=>10, "weight"=>23}, {"id"=>15, "weight"=>30}, {"id"=>6, "weight"=>43}]
# Expected output = [{"id"=>11, "weight"=>5}, {"id"=>12, "weight"=>5}, {"id"=>10, "weight"=>23}, {"id"=>15, "weight"=>30}, {"id"=>6, "weight"=>43}]
In the above example, id = 12 and id = 11 have the duplicate values. I need to have id = 11 before id = 12 in the array. I really appreciate some guidance on this. Thank you!
You can use Enumerable#sort_by with an array too. Note the order of the values in the array.
arr.sort_by {|k| k.values_at("weight", "id") }
Quote from the docs of Array#<=> about how comparison of array works:
Arrays are compared in an “element-wise” manner; the first element of ary is compared with the first one of other_ary using the <=> operator, then each of the second elements, etc… As soon as the result of any such comparison is non zero (i.e. the two corresponding elements are not equal), that result is returned for the whole array comparison.
You can use Array#sort with the spaceship operator like so:
arr.sort do |a,b|
if a["weight"] == b["weight"]
a["id"] <=> b["id"]
else
a["weight"] <=> b["weight"]
end
end

Retrieving values from corresponding keys in Ruby hash

The following only prints white lines. If this isn't the way to retrieve values from keys in Ruby, what is?
numbers = []
for i in 1..100 do
hash = {
:FizzBuzz => 1,
:Prime => 3,
:Fibonacci => 5
}
numbers << { i => hash }
end
numbers.each do |number|
puts number[:Prime]
end
Note this is a MCVE, in the final application 1, 3 and 5 will be function calls.
Try it
For those wondering what I was trying to do, the final (non-MCVE) result can be found on Code Review.
After the first loop, numbers is an array like this:
[
{ 1 => { :FizzBuzz => 1, :Prime => 3, :Fibonacci => 5 } },
{ 2 => { :FizzBuzz => 1, :Prime => 3, :Fibonacci => 5 } },
...
{ 100 => { :FizzBuzz => 1, :Prime => 3, :Fibonacci => 5 } }
]
number[:Prime] is trying to fetch the :Prime element out of your first array element, but you only have the key 1.
The end result is, you print empty lines, as nothing is found at each iteration. Hash access is okay; it is the logic of your code that is the problem (and you did not explain what you are trying to do precisely).
Each number is a hash of this form:
{1=>{:FizzBuzz=>1, :Prime=>3, :Fibonacci=>5}}
which has a number as the sole key. When you look for a hash key that does not exist using Hash#[], Ruby returns nil. And puts nil prints a blank line.
Indeed Hash#[] is the way to retrieve values from a hash.
The keys of number are all integers (coming from i). The symbol :Prime is not a key. Therefore the results are all nil.

How to find the position of an entry within an array of hashes

I have the following hash:
locals_arr = [{:rank=>1, :id=>6}, {:rank=>2, :id=>8}, {:rank=>3, :id=>7}]
I am able to search for a specific entry by id using:
locals_arr.select{|x| x[:id].equal? 7}
which returns something like:
=> [{:rank=>3, :id=>7}]
Now I would like to know the position of this entry.
Sometimes I have a filtered set of ranks and need to know the rank within that set.
Use Array#index for this.
locals_arr = [{:rank=>1, :id=>6}, {:rank=>2, :id=>8}, {:rank=>3, :id=>7}]
locals_arr.index{|h| h[:id]==6}
# => 0
locals_arr.index{|h| h[:id]== 8 }
# => 1
locals_arr.index({:rank=>3, :id=>7})
http://ruby-doc.org/core-2.0.0/Array.html
If you want to get both the index AND the object itself you can use each_with_index and then jump out when you find the right one with break, like so:
irb(main):003:0> locals_arr = [{:rank=>1, :id=>6}, {:rank=>2, :id=>8}, {:rank=>3, :id=>7}]
irb(main):007:0> item, index = *locals_arr.each_with_index {|hash,i| break [hash,i] if hash[:id].equal? 7}
=> [{:rank=>3, :id=>7}, 2]
irb(main):008:0> item
=> {:rank=>3, :id=>7}
irb(main):009:0> index
=> 2
You can use the index metod:
locals_arr = [{:rank=>1, :id=>6}, {:rank=>2, :id=>8}, {:rank=>3, :id=>7}]
locols_arr.index(locals_arr.select{|x| x[:id].equal? 7}[0])
# => 2
or shorter:
locals_arr.index{|e| e[:id] == 7}
# => 2

What is the common way to handle optional method params?

I have a method in which i want to pass in dynamic params. The method is called in a loop and sometimes value2 is available and sometimes not.
What is the common way to handle optional method params?
my_method(:value1 => 1,
:value2 => 2 if foo, # this is not working
:value3 => 3)
I usually create a hash like this:
opts = {:value1 => 1,
:value3 => 3}
opts[:value2] = 2 if foo
my_method(opts)
The benefit of this approach is that everyone catches the if foo as it is a special case. Otherwise many programmers, like myself, will miss this at first glance and get confused why :value2 is not set.
Sometimes you have default settings, then you can use this approach:
default = {:value1 => 0,
:value2 => 0,
:value3 => 0}
opts = {:value1 => 1,
:value3 => 3}
my_method(default.merge(opts))
Or even better:
DEFAULT_OPTS = {:value1 => 0,
:value2 => 0,
:value3 => 0}
def my_method(opts)
opts = DEFAULT_OPTS.merge(opts)
# ...
end
my_method(...)

Multiple Key-Value pairs in a Hash in Ruby

EDIT:
In Short
I have 3 attributes for a single entity, and I have about 100 such entities. I need a good data structure to store them and retrieve them efficiently.
Example:
Lets consider an image with 100 pixels.
Each pixel has three attributes - Red, Green and Blue. I need to store the entire image in terms of its pixels and its RGB values in a data structure like Hash.
An example data structure I was thinking of was something like this:
x={[:red => 1, :green => 2, :blue => 3],[:red => 21, :green => 21, :blue => 32], [:red => 21, :green => 21, :blue => 32]}
My question:
1) Is there a better way to store such sets of data?
2) Is there an efficient way to access such sets data?
In other words, What's the most easiest and efficient way to store multiple key-value pair sets of data and access them efficiently?
Disclaimer: I'm a newbie to Ruby (made some 50% progress).
Thank you.
I think this is what you're asking so please clarify if I'm off base. You want a quick and easy way to take a hash and turn it into a object with methods like x.red, correct? An OpenStruct might be the answer:
require 'ostruct'
hash = { :red => 1, :green => 2, :blue => 3 }
colorset = OpenStruct.new(hash)
Then you can call:
colorset.red + colorset.green + colorset.blue
And get:
=> 6
EDIT:
Based on your comments, forget the above, I think you simply need nested hashes with meaningful keys:
colors = { 'fuschia' => { 'red'=> 1 , 'green' => 2, 'blue' => 3 },
'goldenrod' => { 'red'=> 2, 'green' => 3, 'blue'=> 4 } }
Then access values like this:
colors['fuschia']['red']
=> 1

Resources