Dynamic hash - Ruby on Rails [closed] - ruby-on-rails

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
the code so far:
def queryConnect
...
count = 0
#clients is an array of hashes.
#channels is an array of hashes.
channel_array = []
channels.each do |channel|
begin
if clients[count]["cid"].to_i == channel["cid"].to_i
channel_array << { "cname" => channel["channel_name"].transform_escape,
"name" => clients[count]["client_nickname"].transform_escape }
count = count + 1
else
channel_array << { "cname" => channel["channel_name"].transform_escape }
end
rescue
#transform_escape -> some elements are not type string.
end
end
count = 0
channel_array
end
channels sample input: [{"channel_name" => "game1", "cid" => 1}, {"channel_name" => "game2", "cid" => 2}, {"channel_name" => "game3", "cid" => 3}]
clients sample input: [{"client_nickname" => "greg", "cid" => 1}, {"client_nickname" => "bob", "cid" => 1}, {"client_nickname" => "george", "cid" => 3}]
expected output:[{"cname" => "game1", "name" => ["greg", "bob"]}]
The problem: I'm trying to loop through all the channels, if the client's channel id and channel's channel id (cid) match I want to create a hash with the channel name and client name(s) and then push it onto the channel_array.
The only issue I'm having now is when there are multiple clients in a channel... How can I dynamically add a key, value pair by iterating through the clients array and still maintaining the condition to see if the channel ids match?
I've been beating my head against my text editor for two days on this problem any help is appreciated.

channels = [{"channel_name" => "game1", "cid" => 1},
{"channel_name" => "game2", "cid" => 2},
{"channel_name" => "game3", "cid" => 3}]
clients = [{"client_nickname" => "greg", "cid" => 1},
{"client_nickname" => "bob", "cid" => 1},
{"client_nickname" => "george", "cid" => 3}]
channel_data = channels.inject({}){|m, c| m[c["cid"]] = c["channel_name"]; m}
=> {1 => "game1", 2 => "game2", 3 => "game3"}
client_data = clients.inject({}){ |m, c|
m[c["cid"]] ||= []
m[c["cid"]] << c["client_nickname"]
m
}
=> { 1 => ["greg", "bob"], 3 => ["george"] }
channel_data.map{|cid, cname|
{"cname" => cname, "name" => client_data[cid]} if client_data[cid]
}.compact
=> [{"cname" => "game1", "name" => ["greg", "bob"]}, {"cname" => "game3", "name" => ["george"]}]

Finally figured it out.
channel_array << { "cname" => channel["channel_name"], "name" => clients.inject([]){ |m, c| m << c["client_nickname"] if c["cid"] == channel["cid"]; m} }

Related

Grabbing a specific hash in an array that meets a specific criteria

I have a huge array full of a bunch of hashes. What I need to do is single out one index hash from the array that meets a specific criteria. (doing this due to an rspec test, but having trouble singling out one of them)
My array is like this
[
{
"name" => "jon doe",
"team" => "team2",
"price" => 2000,
"eligibility_settings" => {}
},
{
"name" => "jonny doe",
"team" => "team1",
"value" => 2000,
"eligibility_settings" => {
"player_gender" => male,
"player_max_age" => 26,
"player_min_age" => 23,
"established_union_only" => true
}
},
{
"name" => "jonni doe",
"team" => "team3",
"price" => 2000,
"eligibility_settings" => {}
},
]
I need to single out the second one, based on its eligibility settings. I just took three of them from my array, have lots more, so simple active record methods like (hash.second) won't work in this instance.
I've tried things like
players.team.map(&:hash).find{ |x| x[ 'eligibility_settings?' ] == true}
However when I try this, I get a nil response. (which is odd)
I've also looked into using the ruby detect method, which hasn't gotten me anywhere either
Players.team.map(&:hash).['hash.seligibiltiy_settings'].detect { true }
Would anybody have any idea what to do with this one?
Notes
players.team.map(&:hash).find{ |x| x[ 'eligibility_settings?' ] == true}
Players.team.map(&:hash).['hash.seligibiltiy_settings'].detect { true }
Is is players or Players ?
Why is it plural?
If you can call map on team, it probably should be plural
Why do you convert to a hash?
eligibility_settings? isn't a key in your hash. eligibility_settings is
eligibility_settings can be a hash, but it cannot be true
If you want to check if it isn't empty, use !h['eligibility_settings'].empty?
Possible solution
You could use :
data = [
{
'name' => 'jon doe',
'team' => 'team2',
'price' => 2000,
'eligibility_settings' => {}
},
{
'name' => 'jonny doe',
'team' => 'team1',
'value' => 2000,
'eligibility_settings' => {
'player_gender' => 'male',
'player_max_age' => 26,
'player_min_age' => 23,
'established_union_only' => true
}
},
{
'name' => 'jonni doe',
'team' => 'team3',
'price' => 2000,
'eligibility_settings' => {}
}
]
p data.find { |h| !h['eligibility_settings'].empty? }
# {"name"=>"jonny doe", "team"=>"team1", "value"=>2000, "eligibility_settings"=>{"player_gender"=>"male", "player_max_age"=>26, "player_min_age"=>23, "established_union_only"=>true}}
If h['eligibility_settings'] can be nil, you can use :
data.find { |h| !h['eligibility_settings'].blank? }
or
data.find { |h| h['eligibility_settings'].present? }

Ruby sort by hash and value

I have data like this:
hash_data = [
{:key1 => 'value4', :sortby => 4},
{:key1 => 'valuesds6', :sortby => 6},
{:key1 => 'valuedsd', :sortby => 1},
{:key1 => 'value2_data_is_here', :sortby => 2}
]
I want to sort it to this by the key sortby
hash_data = [
{:key1 => 'valuedsd', :sortby => 1},
{:key1 => 'value2_data_is_here', :sortby => 2},
{:key1 => 'value4', :sortby => 4},
{:key1 => 'valuesds6', :sortby => 6}
]
I have tried using bubble sort, but is there any inbuilt function in a Hash class for such purposes?
Enumerable#sort_by to the rescue:
hash_data.sort_by { |hash| hash[:sortby] }
#=> [{:key1=>"valuedsd", :sortby=>1}, {:key1=>"value2_data_is_here", :sortby=>2}, {:key1=>"value4", :sortby=>4}, {:key1=>"valuesds6", :sortby=>6}]
If you don't care about initial object, I would suggest using Array#sort_by! to modify inplace - it is more resource-efficient:
hash_data.sort_by! { |hash| hash[:sortby] }
If you have different types of data as values to sortby key, you should first unify the data type and only then perform sorting.
To have array sorted in descending order, use Enumerable#reverse (or reverse!):
hash_data.sort_by {|hash| hash[:sortby] }.reverse
#=> [{:key1=>"valuesds6", :sortby=>6}, {:key1=>"value4", :sortby=>4}, {:key1=>"value2_data_is_here", :sortby=>2}, {:key1=>"valuedsd", :sortby=>1}]
Another option for sorting in descending order is the following - note minus sign (credits to #sagarpandya82):
hash_data.sort_by {|hash| -hash[:sortby] }

How to find a hash key containing a matching value for a hash having array of values

I have situation like this:
clients = {
"yellow"=>[{"client_id"=>"2178","price" => 1},{"client_id" => "2282","price" => 2}],
"orange"=>[{"client_id"=>"2180","price" => 1},{"client_id" => "2283","price" => 3}],
"red"=>[{"client_id"=>"2178","price" => 1},{"client_id" => "2282","price" => 3}],
"blue"=>[{"client_id"=>"2180","price" => 1},{"client_id" => "2283","price" => 1}]
}
I need to get the key(s) with client ids => [2282,2178] and get the lowest key value based on price.
How can I achieve this?
res = []
client.each{|k, v|
res << k if v.detect{|hash| hash["client_id"] == "2282"}
}
res
#=> ["red", "yellow"]
NOTE
This answer is for OPs original question that required finding keys that contained "client_id" = 2282. I have not updated my answer as OP changed the requirement quite lazily.

What is an elegant way of modifying hashes inside an array within a nested hash in Ruby

I would like to transform this
def some_process(k,v)
return "#{v}_#{k}"
end
a_hash = {
"i_love_hashes" => {
"thing" => 20,
"other_thing" => "0",
"yet_another_thing" => "i disagree",
"_peculiar_thing" => [
{"count" => 30,
"name" => "freddie"},
{"count" => 15,
"name" => "johhno"},
{"count" => 12,
"name" => "mohammed"},
]
},
"as_do_i" => {
"thing" => 10,
"other_thing" => "2",
"yet_another_thing" => "i strongly agree",
"_peculiar_thing" => [
{"count" => 10,
"name" => "frodo"},
{"count" => 4,
"name" => "bilbo"},
{"count" => 2,
"name" => "elizabeth"},
]
}
}
into this
{
"i_love_hashes"=>{
"thing"=>20,
"other_thing"=>"0",
"yet_another_thing"=>"i disagree",
"_peculiar_thing"=> [
{"count"=>30, "name"=>"freddie", :sinister_name=>"freddie_i_love_hashes"},
{"count"=>15, "name"=>"johhno", :sinister_name=>"johhno_i_love_hashes"},
{"count"=>12, "name"=>"mohammed", :sinister_name=>"mohammed_i_love_hashes"}
]},
"as_do_i"=>{
"thing"=>10,
"other_thing"=>"2",
"yet_another_thing"=>"i strongly agree",
"_peculiar_thing"=>[
{"count"=>10, "name"=>"frodo", :sinister_name=>"frodo_as_do_i"},
{"count"=>4, "name"=>"bilbo", :sinister_name=>"bilbo_as_do_i"},
{"count"=>2, "name"=>"elizabeth", :sinister_name=>"elizabeth_as_do_i"}
]
}
}
this is the code I am currently using to achieve this
a_hash.each_with_object({}) do |(k,v),o|
o.merge!({k =>
v.each_with_object({}) do |(a,b),g|
g.merge!({ a =>
(b.is_a?(Array) ? b.collect {|x| x.merge({sinister_name: (some_process k, x["name"])})} : b)
})
end
})
end
Ignoring the specific details of what is being returned by "some_process" (what is important is that it depends on the outer most key and the inner name values, in this example), are there any alternatives that would be considered more elegant?
Why not do a recursive function?
def add_siniter(hash)
hash[:siniter_name] = "#{hash['name']}_i_love_hashes"
hash
end
def format_hash(item)
case item
when Hash then item.keys.each{|key| format_hash(item[key])}
when Array then item.map!{|h| add_siniter(h)}
end
end
format_hash(a_hash)

How do I get the unique elements and count from an array of hashes in Ruby?

I have an array of hashes, and I want the unique values and count out of it.
I have:
a = [{:key => 2}, {:key => 1}, {:key => 4}, {:key => 1}]
I want see:
a = [{:key => 2}, {:key => 1, :count =>2}, {:key => 4}]
Try this:
a = [{:key => 2}, {:key => 1}, {:key => 4}, {:key => 1}]
b = a.clone
a.uniq!
a.inject([]) { |result,h| h[:count]=b.count(h) if b.count(h) > 1 ; result << h; result }
=> [{:key=>2}, {:key=>1, :count=>2}, {:key=>4}]
You will just have to iterate over the array and count how many times each key occurs, and then sum it all together by building a new array with the result.
The below code snippet should do it.
a = [{:key => 2}, {:key => 1}, {:key => 4}, {:key => 1}]
counts = Hash.new(0)
a.map do |item|
counts[item[:key]] += 1
end
a = counts.collect do |key, count|
if count > 1
{:key => key, :count => count}
else
{:key => key}
end
end
Try this:
a.each {|h| count = a.count(h); if count > 1; dup_h = h.clone; h.merge!({:count => count}); a.delete(dup_h); end }
The most concise way there is (I think) is this:
a.uniq.map{|hsh| c = a.count(hsh); c == 1 || hsh[:count] = c; hsh}
or if you don't want to modify the hashes in a:
a.uniq.map{|hsh| h = hsh.dup; c = a.count(hsh); c == 1 || h[:count] = c; h}
A (marginally) prettier solution that yields a different result is:
a.uniq.group_by{|hsh| a.count(hsh)}
#=> {1=>[{:key=>2}, {:key=>4}], 2=>[{:key=>1}]}
Check out the documentation on Enumerable#count and Enumerable#group_by to learn more of what you can do with those.
Ruby's core is actually quite powerful.

Resources