I have an array of hashes of the form:
[{"status"=>"Unshipped", "city"=>"thane", "buyer_name"=>"abc", "name"=>"abc def", "countryCode"=>"IN", "payment_method"=>"COD", "order_type"=>"StandardOrder", "y_id"=>"r123", "phone"=>"12345", "state"=>"Maharashtra", "service"=>"Expedited", "address_1"=>"abc xyz", "address_2"=>"yyyy", "postalCode"=>"400607", "shipped_by_y"=>"false", "channel"=>"MFN", "amout"=>"350.00"}, {"status"=>"Unshipped", "city"=>"Chembur, Mumbai", "buyer_name"=>"xyz", "name"=>"xyz lmn", "countryCode"=>"IN", "payment_method"=>"Other", "order_type"=>"StandardOrder", "y_id"=>"r124", "phone"=>"12436", "state"=>"Maharashtra", "service"=>"Expedited", "address_1"=>"add 1", "address_2"=>"add 2", "postalCode"=>"400071", "shipped_by_y"=>"false", "channel"=>"MFN", "amout"=>"399.00"}]
From this array I want to select the element which has given value for the key y_id.
Eg. if I want to get the element with 'y_id' = 'r124', I should get :
{"status"=>"Unshipped", "city"=>"Chembur, Mumbai", "buyer_name"=>"xyz", "name"=>"xyz lmn", "countryCode"=>"IN", "payment_method"=>"Other", "order_type"=>"StandardOrder", "y_id"=>"r124", "phone"=>"12436", "state"=>"Maharashtra", "service"=>"Expedited", "address_1"=>"add 1", "address_2"=>"add 2", "postalCode"=>"400071", "shipped_by_y"=>"false", "channel"=>"MFN", "amout"=>"399.00"}
You should use the find API. It returns the first element which matches what you look for, or nil if none is found:
arr.find { |element| element['y_id'] == 'r124' }
will return:
{"status"=>"Unshipped", "city"=>"Chembur, Mumbai", "buyer_name"=>"xyz", "name"=>"xyz lmn", "countryCode"=>"IN", "payment_method"=>"Other", "order_type"=>"StandardOrder", "y_id"=>"r124", "phone"=>"12436", "state"=>"Maharashtra", "service"=>"Expedited", "address_1"=>"add 1", "address_2"=>"add 2", "postalCode"=>"400071", "shipped_by_y"=>"false", "channel"=>"MFN", "amout"=>"399.00"}
You should be able to use
arr.find { |i| i['y_id] == *value* }
Which will iterate over your array of hashes, where i is any given hash. Select will return an array of objects where the block resolves to true.
So, for example if you passed in r123 as value, it would return
{"status"=>"Unshipped", "city"=>"thane", "buyer_name"=>"abc", "name"=>"abc def", "countryCode"=>"IN", "payment_method"=>"COD", "order_type"=>"StandardOrder", "y_id"=>"r123", "phone"=>"12345", "state"=>"Maharashtra", "service"=>"Expedited", "address_1"=>"abc xyz", "address_2"=>"yyyy", "postalCode"=>"400607", "shipped_by_y"=>"false", "channel"=>"MFN", "amout"=>"350.00"}
to get the hash. (Thanks #BroiSatse)
Related
I need the index of an array in a multidimensional array if it contains a unique string.
array:
[
{:id=>5, :name=>"Leaf Green", :hex_value=>"047115"},
{:id=>15, :name=>"Lemon Yellow", :hex_value=>"FFF600"},
{:id=>16, :name=>"Navy", :hex_value=>"285974"}
]
If hex_value of 'FFF600' exists, return the arrays position, which in this case would be 1.
This is where I am at, but it's returning [].
index = array.each_index.select{|i| array[i] == '#FFF600'}
That's returning nil, because there's no element i (index) in the array with value #FFF600 (nor FFF600), you need to access to the hex_value key value:
p [
{:id=>5, :name=>"Leaf Green", :hex_value=>"047115"},
{:id=>15, :name=>"Lemon Yellow", :hex_value=>"FFF600"},
{:id=>16, :name=>"Navy", :hex_value=>"285974"}
].yield_self { |this| this.each_index.select { |index| this[index][:hex_value] == 'FFF600' } }
# [1]
Giving you [1], because of using select, if you want just the first occurrence, you can use find instead.
I'm using yield_self there, to avoid assigning the array to a variable. Which is equivalent to:
array = [
{:id=>5, :name=>"Leaf Green", :hex_value=>"047115"},
{:id=>15, :name=>"Lemon Yellow", :hex_value=>"FFF600"},
{:id=>16, :name=>"Navy", :hex_value=>"285974"}
]
p array.each_index.select { |index| array[index][:hex_value] == 'FFF600' }
# [1]
Being Ruby, you can use the method for that: Enumerable#find_index
p [
{:id=>5, :name=>"Leaf Green", :hex_value=>"047115"},
{:id=>15, :name=>"Lemon Yellow", :hex_value=>"FFF600"},
{:id=>16, :name=>"Navy", :hex_value=>"285974"}
].find_index { |hash| hash[:hex_value] == 'FFF600' }
# 1
I have an array of hash, sorting by particular key not properly working,
The array of hash is:
#final_array = [{:Region=>"region - 1", :ItemSize=>"Box", :Price=>""}, {:Region=>"region - 1", :ItemSize=>"Pack", :Price=>""}, {:Region=>"region - 1", :ItemSize=>"ball", :Price=>""}, {:Region=>"region - 1", :ItemSize=>"ball -1", :Price=>""}, {:Region=>"region - 1", :ItemSize=>"new size", :Price=>""}, {:Region=>"region - 1", :ItemSize=>"new size 1", :Price=>""}, {:Region=>"region - 1", :ItemSize=>"wels", :Price=>""}]
#final_array = #final_array.sort_by { |x, y| x[:ItemSize] }
After sorting I am checking array by select query.
a = []
#final_array.select{ |x, y| a << x[:ItemSize] }
a
# => ["Box", "Pack", "ball", "ball -1", "new size", "new size 1", "wels"]
It's not properly working.
How do I solve this problem?
#final_array = #final_array.sort_by { |x, y| x[:ItemSize].downcase }
This makes sure that the case you pass into sort_by is all the same. It does not change the case of the ItemSize values.
If you compare 2 strings for sorting with just str1 <=> str2, upcase letters comes before downcase letter: A B C ... Y Z a b c ... y z. That's why you get Box and Pack before ball.
Turn everything to the same case if you want' it case insensitive.
#final_array.sort_by { |x, y| x[:ItemSize].downcase }
Anyway, I personally don't like sorting hashed, I would better get the values I need as an array and then order that array.
ordered = #final_array.map{|x| x[:ItemSize] }.sort_by{|x| x.downcase }
You can try in following way:
sorted_arr = #final_array.collect{|arr| arr[:ItemSize]}.sort { | a1, a2 | a1.downcase <=> a2.downcase }
I have a multi-dimensional array that contains a lot of information about various objects. I'm looking to remove all but the first instance of arrays that contain similar information:
multi_array = [
["Nissan", "Altima", "tan", "2016", "80000"],
["Ford", "F-150", "silver", "2012", "120000"],
["Nissan", "Altima", "red", "2009", "50000"],
["Audi", "A4", "blue", "2014", "30000"]
]
In the above example I want to remove any of the subarrays that have the instance of "Altima" in it so that result would be:
fixed_multi_array = [
["Nissan", "Altima", "tan", "2016", "80000"],
["Ford", "F-150", "silver", "2012", "120000"],
["Audi", "A4", "blue", "2014", "30000"]
]
What's the fastest way to do this in ruby (or Ruby on Rails)?
Update:
Should have clarified, I'm looking to de-duplicate based on a value that's always in the same position of the sub-arrays. So, in example above I'm always looking to de-dupe only on value in position 1 of the sub-arrays.
You can use uniq:
fixed_multi_array = multi_array.uniq{|x| x[1]}
Demonstration
Here is one more way to do this:
multi_array.group_by {|i| i[1]}.values.map(&:first)
I have included the given code:
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
#amaze = params[:test] #I get "1,3,7"
I get this, now please guide me how to match keys with #amaze and accordingly fetch its values from #classes i.e USA, France, Delhi.
Since #amaze is just a String, lets first convert it in Array so its easy to enumerate:
#amaze = "1,3,7"
#amaze = #amaze.split(",")
# => ["1", "3", "7"]
Now, since you have all keys extract all values:
#amaze.map { |i| #classes[i.to_i] }
# => ["USA", "France", "Delhi"]
Split #amaze by , and get an array of keys, convert them into Integer, then select only those key/value pairs which key is into this array of keys. Something like this:
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
#amaze = "1,3,7" #I get "1,3,7"
arr = #amaze.split(',').map(&:to_i)
p #classes.select{|el| arr.include? el}
Result:
#> {1=>"USA", 3=>"France", 7=>"Delhi"}
If you want values only use .values:
p #classes.select{|el| arr.include? el}.values
Result:
#> ["USA", "France", "Delhi"]
For what(seemingly) you are asking, the below line will do it:
#amaze.split(",").each { |i| p #classes[i.to_i] }
# If #amaza = "1,3,7", above line will output:
# => "USA"
# "France"
# "UK"
This should work well for you:
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
#amaze = params[:test].split(",").map(&:to_i)
#classes.values_at(*#amaze)
#=> ["USA", "France", "Delhi"]
Hash#values_at accepts an indefinite number of keys and returns their values as an array. The * (splat) operator explodes the array so this call actually becomes #classes.values_at(1,3,7) Docs
Might also want to add a compact to the end in the event a key does not exist. e.g
#amaze = params[:test].split(",").map(&:to_i) # Asssume this returns [1,3,7,9]
#classes.values_at(*#amaze)
#=> ["USA", "France", "Delhi",nil]
#classes.values_at(*#amaze).compact
#=> ["USA", "France", "Delhi"]
I think a clearer understanding of hashes would help you out here.
A Hash is a data structure that is a list of key-value pairs. For example, the following is a Hash object of key-value pairs (your example):
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
If you want to extract a value from #classes, you need to pass the key of the value you want. If we wanted "USA" we would pass the key of 1 to #classes. If we wanted "France", we would pass it the key of 3:
#classes[1] would return "USA" and #classes[3] would return "France".
It's not clear what data structure #amaze is according to your question, but let's say it's the string "1, 3, 7" which we can split to create an array [1, 3, 7].
You could iterate over the array to get each of the values from #classes:
#amaze.split(",").map(&:to_i).each do |key|
puts #classes[key]
end
That would print out each of the corresponding values to keys in #classes.
I have an array that behaves like a multidimensional array through spaces, like:
"roles"=>["1 editor 0", "1 editor 1", "2 editor 0", "2 editor 1", "14 editor 0", "15 editor 0"], "commit"=>"Give Access", "id"=>"3"}
Each array value represents [category_id, user.title, checked_boolean], and comes from
form
<%= hidden_field_tag "roles[]", [c.id, "editor", 0] %>
<%= check_box_tag "roles[]", [c.id, "editor", 1 ], !!checked %>
which I process it using splits
params[:roles].each do |role|
cat_id = role[0].split(" ")[0]
title = role.split(" ")[1]
checked_boolean = role.split(" ")[2]
end
Given the array at the top, you can see that the "Category 1" & "Category 2" is checked, while "Cat 14" and "Cat 15" are not.
I would like to compare the values of the given array, and if both 1 & 0 exists for a given category_id, I would like to get rid of the value with "checked_boolean = 0". This way, if the boolean is a 1, I can check to see if the Role already exists, and if not, create it. And if it is 0, I can check to see if Role exists, and if it does, delete it.
How would I be able to do this? I thought of doing something like params[:roles].uniq but didn't know how to process the uniq only on the first split.
Or is there a better way of posting the "unchecks" in Rails? I've found solutions for processing the uncheck action for simple checkboxes that passes in either true/false, but my case is different because it needs to pass in true/false in addition to the User.Title
Let's params[:roles] is:
["1 editor 0", "1 editor 1", "2 editor 0", "2 editor 1", "14 editor 0", "15 editor 0"]
The example of the conversion and filtering is below:
roles = params[:roles].map {| role | role.split " " }
filtered = roles.select do| role |
next true if role[ 2 ].to_i == 1
count = roles.reduce( 0 ) {| count, r | r[ 0 ] == role[ 0 ] && count + 1 || count}
count == 1
end
# => [["1", "editor", "1"], ["2", "editor", "1"], ["14", "editor", "0"], ["15", "editor", "0"]]
filtered.map {| role | role.join( ' ' ) }
Since the select method returns a new filtered role array, so result array you can see above. But of course you can still use and source params[:roles], and intermediate (after map method worked) versions of role array.
Finally you can adduce the result array into the text form:
filtered.map {| role | role.join( ' ' ) }
=> ["1 editor 1", "2 editor 1", "14 editor 0", "15 editor 0"]
majioa's solution is certainly more terse and a better use of the language's features, but here is my take on it with a more language agnostic approach. I have only just started learning Ruby so I used this as an opportunity to learn, but it does solve your problem.
my_array = ["1 editor 0", "1 editor 0", "1 editor 1", "2 editor 0",
"2 editor 1", "14 editor 0", "15 editor 0"]
puts "My array before:"
puts my_array.inspect
# As we're nesting a loop inside another for each loop
# we can't delete from the same array without confusing the
# iterator of the outside loop. Instead we'll delete at the end.
role_to_del = Array.new
my_array.each do |role|
cat_id, checked_boolean = role.split(" ")[0], role.split(" ")[2]
if checked_boolean == "1"
# Search through the array and mark the roles for deletion if
# the category id's match and the found role's checked status
# doesn't equal 1.
my_array.each do |s_role|
s_cat_id = s_role.split(" ")[0]
if s_cat_id != cat_id
next
else
s_checked_boolean = s_role.split(" ")[2]
role_to_del.push s_role if s_checked_boolean != "1"
end
end
end
end
# Delete all redundant roles
role_to_del.each { |role| my_array.delete role }
puts "My array after:"
puts my_array.inspect
Output:
My array before:
["1 editor 0", "1 editor 0", "1 editor 1", "2 editor 0", "2 editor 1", "14 editor 0",
"15 editor 0"]
My array after:
["1 editor 1", "2 editor 1", "14 editor 0", "15 editor 0"]