I have two hashes, one looks like this:
{:id => "SG_5viWPcG0SLvszXbBxogLkT_51.514568_-0.126244#1300740367",
:name => "Shellys Shoes",
:lat => 51.5145683289,
:lng => -0.1262439936}
This is just one record, there are about 80,
The second hash I have is:
{"id":"SG_2zNWLdG9147g2ROvNWpDHr_51.512360_0.124480#1300740823",
"lat":51.5123596191,
"lng":-0.1244800016}
The hash above is again just one record, however it is a product of the HASH above after going through an API that does not return all the records, only the valid ones, what I want to do is compare the top hash with the bottom one and delete any records that are not present in the bottom hash,
for example if id:SG_5viWPcG0SLvszXbBxogLkT_51.514568_-0.126244#1300740367 is not in the the second hash then delete that record,
I can compare the hashes, but cant see how to delete if ID is not present?
Thanks guys!
edit:
these are the returned values....
{"points":[{"id":"SG_75oKOgvgFPLjwmdyAKA2rq_51.512825_-0.124655#1300740283","lat":51.5128250122,"lng":-0.1246550009},{"id":"SG_0Sz9CBF5t70tdAffTKYNSg_51.512360_-0.124388#1300740807","lat":51.5123596191,"lng":-0.1243880019},{"id":"SG_2zNWLdG9147g2ROvNWpDHr_51.512360_-0.124480#1300740823","lat":51.5123596191,"lng":-0.1244800016},{"id":"SG_5PvBx89sLPgplapegVJDFv_51.513100_-0.124809#1300740049","lat":51.5130996704,"lng":-0.1248089969},{"id":"SG_4luyHFi5R2f1w3cpjT61ik_51.513393_-0.124556#1300740719","lat":51.5133934021,"lng":-0.1245559976},{"id":"SG_4luyHFi5R2f1w3cpjT61ik_51.513393_-0.124556#1300740719","lat":51.5133934021,"lng":-0.1245559976},{"id":"SG_0pEvrpt7bs42jPAxFSrquC_51.512264_-0.124413#1300740807","lat":51.5122642517,"lng":-0.1244129986},]}
This is the original format:
[ { :id => "SG_2Km6LX3tEcFwx24eotTHIY_51.513016_-0.123721#1300740411",
:name => "French Connection Group Plc",
:lat => 51.5130157471,
:lng => -0.1237210035
}]
You can collect a list of the valid IDs from the returned values with something like:
valid_ids = returned["points"].collect { |point| point["id"] }
You can then remove the invalid values from the original with something like:
original.delete_if { |entry| !valid_ids.include? entry[:id] }
Related
I am currently having trouble writing a test that addresses the eligibility_settings of a record I have. I am having trouble pulling out one of the specific elements from this hash.
Specifically I want to test that by making a change elsewhere in a different function that changes the min age of a specific player, and so what I am really trying to test is the eligibility_settings.min_age. But i'm having trouble within my test isolating that out.
My hash looks like this
{
:name => "player1",
:label => "redTeam_1_1",
:group => "adult",
:teamId => 7,
:eligibility_settings => {
"min_age" => 18,
"player_gender" => "female",
"union_players_only" => true
}
}
However when I try looping through this hash, I am having trouble isolating that one element.
i've tried something like
team.get_players.first.map do |settings, value|
value.tap do |x, y|
y[3]
end
end
However It seems like what i've been trying, and my approach has not been working quite right.
Would anyone have any idea what I could do with this?
Although #SergioTulentsev gave the proper response, in the future if you are going to be looping through hashes, below is one way to iterate through the keys and grab the value you want.
hash = {
:name => "player1",
:label => "redTeam_1_1",
:group => "adult",
:teamId => 7,
:eligibility_settings => {
"min_age" => 18,
"player_gender" => "female",
"union_players_only" => true
}
}
hash.map do |settings, value|
p hash[:eligibility_settings]['min_age'] if settings == :eligibility_settings
end # output 18
Hash to csv
hash :
{
"employee" => [
{
"name" => "Claude",
"lastname"=> "David",
"profile" => [
"age" => "43",
"jobs" => [
{
"name" => "Ingeneer",
"year" => "5"
}
],
"graduate" => [
{
"place" => "Oxford",
"year" => "1990"
},
],
"kids" => [
{
"name" => "Viktor",
"age" => "18",
}
]
}
}]
this is an example of an hash I would work on. So, as you can see, there is many level of array in it.
My question is, how do I put it properly in a CSV file?
I tried this :
column_names = hash['employee'].first.keys
s=CSV.generate do |csv|
csv << column_names
hash['scrap'].each do |x|
csv << x.values
end
end
File.write('myCSV.csv', s)
but I only get name, lastname and profile as keys, when I would catch all of them (age, jobs, name , year, graduate, place...).
Beside, how can I associate one value per case?
Because I actually have all employee[x] which take a cell alone. Is there any parameters I have missed?
Ps: This could be the following of this post
A valid CSV output has a fixed number of columns, your hash has a variable number of values. The keys jobs, graduate and kids could all have multiple values.
If your only goal is to make a CSV output that can be read in Excel for example, you could enumerate your Hash, take the maximum number of key/value pairs per key, total it and then write your CSV output, filling the blank values with "".
There are plenty of examples here on Stack Overflow, search for "deep hash" to start with.
Your result would have a different number of columns with each Hash you provide it.
That's too much work if you ask me.
If you just want to present a readable result, your best and easiest option is to convert the Hash to YAML which is created for readability:
require 'yaml'
hash = {.....}
puts hash.to_yaml
employee:
- name: Claude
lastname: David
profile:
- age: '43'
jobs:
- name: Ingeneer
year: '5'
graduate:
- place: Oxford
year: '1990'
kids:
- name: Viktor
age: '18'
If you want to convert the hash to a CSV file or record, you'll need to get a 'flat' representation of your keys and values. Something like the following:
h = {
a: 1,
b: {
c: 3,
d: 4,
e: {
f: 5
},
g: 6
}
}
def flat_keys(h)
h.keys.reject{|k| h[k].is_a?(Hash)} + h.values.select{|v| v.is_a?(Hash)}.flat_map{|v| flat_keys(v)}
end
flat_keys(h)
# [:a, :c, :d, :g, :f]
def flat_values(h)
h.values.flat_map{|v| v.is_a?(Hash) ? flat_values(v) : v}
end
flat_values(h)
# [1, 3, 4, 5, 6]
Then you can apply that to create a CSV output.
It depends on how those fields are represented in the database.
For example, your jobs has a hash with name key and your kids also has a hash with name key, so you can't just 'flatten' them, because keys have to be unique.
jobs is probably another model (database table), so you probably would have to (depending on the database) write it separately, including things like the id of the related object and so on.
Are you sure you're not in over your head? Judging from your last question and because you seem to treat csv's as simple key-values pair omitting all the database representation and relations.
I have a set of data that is an array of hashes, with each hash representing one record of data:
data = [
{
:id => "12345",
:bucket_1_rank => "2",
:bucket_1_count => "12",
:bucket_2_rank => "7",
:bucket_2_count => "25"
},
{
:id => "45678",
:bucket_1_rank => "2",
:bucket_1_count => "15",
:bucket_2_rank => "9",
:bucket_2_count => "68"
},
{
:id => "78901",
:bucket_1_rank => "5",
:bucket_1_count => "36"
}
]
The ranks values are always between 1 and 10.
What I am trying to do is select each of the possible values for the rank fields (the :bucket_1_rank and :bucket_2_rank fields) as keys in my final resultset, and the values for each key will be an array of all the values in its associated :bucket_count field. So, for the data above, the final resulting structure I have in mind is something like:
bucket 1:
{"2" => ["12", "15"], "5" => ["36"]}
bucket 2:
{"7" => ["25"], "9" => ["68"]}
I can do this working under the assumption that the field names stay the same, or through hard coding the field/key names, or just using group_by for the fields I need, but my problem is that I work with a different data set each month where the rank fields are named slightly differently depending on the project specs, and I want to identify the names for the count and rank fields dynamically as opposed to hard coding the field names.
I wrote two quick helpers get_ranks and get_buckets that use regex to return an array of fieldnames that are either ranks or count fields, since these fields will always have the literal string "_rank" or "_count" in their names:
ranks = get_ranks
counts = get_counts
results = Hash.new{|h,k| h[k] = []}
data.each do |i|
ranks.each do |r|
unless i[r].nil?
counts.each do |c|
results[i[r]] << i[c]
end
end
end
end
p results
This seems to be close, but feels awkward, and it seems to me there has to be a better way to iterate through this data set. Since I haven't worked on this project using Ruby I'd use this as an opportunity to improve my understanding iterating through arrays of hashes, populating a hash with arrays as values, etc. Any resources/suggestions would be much appreciated.
You could shorten it to:
result = Hash.new{|h,k| h[k] = Hash.new{|h2,k2| h2[k2] = []}}
data.each do |hsh|
hsh.each do |key, value|
result[$1][value] << hsh["#{$1}_count".to_sym] if key =~ /(.*)_rank$/
end
end
puts result
#=> {"bucket_1"=>{"2"=>["12", "15"], "5"=>["36"]}, "bucket_2"=>{"7"=>["25"], "9"=>["68"]}}
Though this is assuming that :bucket_2_item_count is actually supposed to be :bucket_2_count.
#models.map(&:attributes)) returns a list of hashes from each column to its value in the db
How do I limit it so that only specific columns are returns (e.g. just name and id?).
Also, how do I combine multiple columns to a new key => value pair? For example, if a user has first_name and last_name, the above would return
[{"first_name" => "foo", "last_name" => "bar"}] but I want it to be [{"name" => "foo bar"}]
How do I achieve this transformation? Thanks!
For the first part (limiting the attributes in the hash):
#models.map {|model| model.attributes.slice(:id, :name)}
For combining multiple attribute into a new attribute, the cleanest way is usually an accessor method:
class User < ActiveRecord::Base
def name
"#{first_name} #{last_name}"
end
end
Then build your hash manually during iteration:
#models.map {|model| {:id => model.id, :name => model.name}}
If you're using more than one attribute from the attributes hash, you can use merge:
#models.map do |model|
model.attributes.slice(:id, :first_name, :last_name).merge(:name => model.name)
end
I believe this has been asked/answered before in a slightly different context, and I've seen answers to some examples somewhat similar to this - but nothing seems to exactly fit.
I have an array of email addresses:
#emails = ["test#test.com", "test2#test2.com"]
I want to create a hash out of this array, but it must look like this:
input_data = {:id => "#{id}", :session => "#{session}",
:newPropValues => [{:key => "OWNER_EMAILS", :value => "test#test.com"} ,
{:key => "OWNER_EMAILS", :value => "test2#test2.com"}]
I think the Array of Hash inside of the hash is throwing me off. But I've played around with inject, update, merge, collect, map and have had no luck generating this type of dynamic hash that needs to be created based on how many entries in the #emails Array.
Does anyone have any suggestions on how to pull this off?
So basically your question is like this:
having this array:
emails = ["test#test.com", "test2#test2.com", ....]
You want an array of hashes like this:
output = [{:key => "OWNER_EMAILS", :value => "test#test.com"},{:key => "OWNER_EMAILS", :value => "test2#test2.com"}, ...]
One solution would be:
emails.inject([]){|result,email| result << {:key => "OWNER_EMAILS", :value => email} }
Update: of course we can do it this way:
emails.map {|email| {:key => "OWNER_EMAILS", :value => email} }