Find index of array in multidimensional array by string value - ruby-on-rails

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

Related

Merge nested array of hashes in ruby

In sample code nameA and nameB missing few dates when comparing with common_count.
So, the expected output hash must be present missing dates with value will be zero
Thank you in advance.
"common_count"=>[
{20190704=>0}, {20190705=>0}, {20190706=>0}, {20190707=>0},
{20190708=>0}, {20190709=>0}, {20190710=>0}, {20190711=>0}
]
}
{
"nameA"=>[
{20190704=>10} {20190706=>50}, {20190707=>10},
{20190708=>0}, {20190709=>10}, {20190710=>0}, {20190711=>40}
],
"nameB"=>[
{20190704=>30}, {20190707=>20},
{20190708=>3}, {20190709=>5}, {20190710=>0}, {20190711=>20}
], ..... etc
}
"nameA"=>[
{20190704=>10}, {20190705=>0}, {20190706=>50}, {20190707=>10},
{20190708=>0}, {20190709=>10}, {20190710=>0}, {20190711=>40}
],
"nameB"=>[
{20190704=>30},{20190705=>0}, {20190706=>0} {20190707=>20},
{20190708=>3}, {20190709=>5}, {20190710=>0}, {20190711=>20}
],...etc
}
You could simply:
entries = {
"common_count"=>[
{"20190704"=>0}, {"20190705"=>0}, {"20190706"=>0}, {"20190707"=>0},
{"20190708"=>0}, {"20190709"=>0}, {"20190710"=>0}, {"20190711"=>0}
],
"nameA"=>[
{"20190704"=>10}, {"20190706"=>50}, {"20190707"=>10},
{"20190708"=>0}, {"20190709"=>10}, {"20190710"=>0}, {"20190711"=>40}
],
"nameB"=>[
{"20190704"=>30}, {"20190707"=>20},
{"20190708"=>3}, {"20190709"=>5}, {"20190710"=>0}, {"20190711"=>20}
]
}
default = entries.delete("common_count")
default_map = default.inject(:merge)
merged_entries = entries.map do |nameKey, value|
value_map = value.inject(:merge)
merged_value_map = default_map.merge(value_map)
merged_value = merged_value_map.map { |k, v| {k => v} }
{nameKey => merged_value}
end.inject(:merge)
Notice that I had to convert an array of hashes to a single hash in order to merge the hashes' contents. Then I reverted the hash result to an array of hashes.
If you have input like below,
common_count = [{"20190704"=>0}, {"20190705"=>0}, {"20190706"=>0}, {"20190707"=>0}, {"20190708"=>0}, {"20190709"=>0}, {"20190710"=>0}, {"20190711"=>0}]
nameA = [{"20190704"=>10}, {"20190706"=>50}, {"20190707"=>10}, {"20190708"=>0}, {"20190709"=>10}, {"20190710"=>0}, {"20190711"=>40}]
nameB = [{"20190704"=>30}, {"20190707"=>20}, {"20190708"=>3}, {"20190709"=>5}, {"20190710"=>0}, {"20190711"=>20}]
You can run following to add missing keys from common_count,
[nameA, nameB].each do |arr|
(common_count.map(&:keys).flatten - arr.map(&:keys).flatten).each do |missing|
arr.push({missing => 0})
end
end

Rail convert array into group

I am trying to convert one of my array into some format where it can convert itself into table format.
I have an array which is:
[
{
id: 1,
Revenue_Account: "Revenue Receipt",
Amount: 59567,
Year: "2012-13",
created_at: "2018-08-21T06:30:17.000Z",
updated_at: "2018-08-21T06:30:17.000Z"
},
{
id: 2,
Revenue_Account: "Revenue Expenditure ",
Amount: 54466,
Year: "2012-13",
created_at: "2018-08-21T06:30:17.000Z",
updated_at: "2018-08-21T06:30:17.000Z"
},
...
]
Full code of my array link to my actual array
I want this data to be converted into this format:
data: [
{
id: 1,
Sector: "Revenue Receipt",
2012-13: 59567,
2013-14: 68919,
2014-15: 72570,
2015-16: 96123,
2016-17: 105585,
2017-18_BE: 137158,
},
{
id: 2,
Sector: "Revenue Expenditure",
2012-13: 59567,
2013-14: 68919,
2014-15: 72570,
2015-16: 96123,
2016-17: 105585,
2017-18_BE: 137158,
},
....
]
I am using this code to group my array:
group = b.group_by{|data| data[:Revenue_Account]}
this is grouping my data as I am expecting in order to achieve my goal I am trying this code.
group = b.group_by{|data| data[:Revenue_Account]}
du = []
group.each do |i|
du.push({Sector:i[0]})
end
This is giving me Sector wise result how can I add year in my code.
You can't have a single id in there because you're grouping up many entries with different ids, but this is how you'd get the array in the format you're asking for:
grouped = {}
b.each do |x|
grouped[x[:Revenue_Account]] ||= {}
grouped[x[:Revenue_Account]][:Sector] = x[:Revenue_Account]
grouped[x[:Revenue_Account]][x[:Year]] = x[:Amount]
end
return {data: grouped.values}
Which gets you:
{
:data=>[
{
:Sector=>"Revenue Receipt",
"2012-13"=>59567,
"2013-14"=>68919,
"2014-15"=>78417,
"2015-16"=>96123,
"2016-17"=>105585,
"2017-18_BE"=>137158
},
{
:Sector=>"Revenue Expenditure ",
"2012-13"=>54466,
"2013-14"=>62477,
"2014-15"=>72570,
"2015-16"=>83616,
"2016-17"=>94765,
"2017-18_BE"=>122603
},
]
}
We build a new hash by looping through the original hash and creating hash keys if they don't exist. Then we start assigning values as you want them to be in the output. On each iteration, we're creating a new key in this hash for the Revenue_Account value if its the first time we've seen it. Then we assign that particular Revenue_Account's Date and Amount to the output. So for value 'Revenue Receipt' it looks like this:
Grouped hash starts off as empty
On first iteration, we see that group["Revenue Receipt"] is nil, so we initialize it with an empty hash via ||= (assign if nil)
We then assign :Sector => "Revenue Receipt" and this entry's Year and Amount, "2012-13" => 59567
Our grouped hash looks like: {"Revenue Receipt" => {:Sector => "Revenue Receipt", "2012-13" => 59567}
On the next iteration we see that group["Revenue Receipt"] is not nil, so ||= does not override it with an empty hash
We then assign :Sector => "Revenue Receipt" and this entry's Year and Amount, "2012-14" => 68919, which adds a new key/value to the existing hash
Our grouped hash now looks like: {"Revenue Receipt" => {:Sector => "Revenue Receipt", "2012-13" => 59567, "2012-14" => 68919}
After we parse the entire array, we now have a hash that has a key of the Revenue_Account, and values which look like the hash output you're expecting.
We discard the key and return only the hash values, which gets you the final output.
Another option, directly manipulating the array.
array_of_data = array
.each { |h| h[:Sector] = h.delete(:Revenue_Account) }
.each { |h| h[h[:Year]] = h[:Amount]}
.each { |h| h.delete_if{ |k, _| k == :created_at || k == :updated_at || k == :id || k == :Year || k == :Amount} }
.group_by { |h| h[:Sector] }
.values.map { |a| a.inject(:merge) }
Then just:
h = {}
h[:data] = array_of_data
To understand what happens along the code, just ad line by line outputting the result, like:
p array
.each { |h| h[:Sector] = h.delete(:Revenue_Account) }
Then:
p array
.each { |h| h[:Sector] = h.delete(:Revenue_Account) }
.each { |h| h[h[:Year]] = h[:Amount]}
Etcetera...
To understand .inject(:merge), see here Rails mapping array of hashes onto single hash

Search and extract results from an array based on an entry keyword in ruby

I would like to compare an Array to an element and extract those data in the another Array
Here is an example of data i'm working with:
Array = [{:id=>3, :keyword=>"happy", :Date=>"01/02/2016"},
{:id=>4, :keyword=>"happy", :Date=>"01/02/2016"} ... ]
for example i want the first keyword happy to search the same array ,extract if there's any similar words and put them inside another array here is what i'm looking for an end result:
Results = [{:keyword=>happy, :match =>{
{:id=>3, :keyword=>"happy", :Date=>"01/02/2016"}... }]
Here is the first part of the code :
def relationship(file)
data = open_data(file)
parsed = JSON.parse(data)
keywords = []
i = 0
parsed.each do |word|
keywords << { id: i += 1 , keyword: word['keyword'].downcase, Date: word['Date'] }
end
end
def search_keyword(keyword)
hash = [
{:id=>1, :keyword=>"happy", :Date=>"01/02/2015"},
{:id=>2, :keyword=>"sad", :Date=>"01/02/2016"},
{:id=>3, :keyword=>"fine", :Date=>"01/02/2017"},
{:id=>4, :keyword=>"happy", :Date=>"01/02/2018"}
]
keywords = []
hash.each do |key|
if key[:keyword] == keyword
keywords << key
end
end
keywords
#{:keyword=> keyword, :match=> keywords}
end
search_keyword('fine')
#search_keyword('sad')
You could group the match elements by key (:match) then get the result with a single hash lookup.
Here's another idea that could help you with your situation using enumerable and index:
array to be search:
array = [
{:id=>3, :keyword=>"happy", :Date=>"01/02/2016"},
{:id=>4, :keyword=>"happy", :Date=>"01/02/2016"},
{:id=>1, :keyword=>"happy", :Date=>"01/02/2015"},
{:id=>2, :keyword=>"sad", :Date=>"01/02/2016"},
{:id=>30, :keyword=>"fine", :Date=>"01/02/2017"},
{:id=>41, :keyword=>"happy", :Date=>"01/02/2018"}
]
method search:
store all element matching the term in the array.
def search(term, array)
array = []
array << {keyword: "#{term}", match: []}
arr.select { |element| array.first[:match] << element if element[:keyword].index(term) }
array
end
Testing:
p search('sa', array)
# => [{:keyword=>"sa", :match=>[{:id=>2, :keyword=>"sad", :Date=>"01/02/2016"}, {:id=>21, :keyword=>"sad", :Date=>"01/02/2016"}]}]
hope that will get you goin!

Merge multidimensional array of hash based on hash key and value in ruby

I have one array and i want to match value of id key with other array of hash in multidimensional array,
input = [
[ {"id"=>"1","name"=>"a"},
{"id"=>"2","name"=>"b"},
{"id"=>"3","name"=>"c"},
{"id"=>"4","name"=>"d"},
{"id"=>"5","name"=>"e"},
{"id"=>"6","name"=>"f"}
],
[ {"id"=>"3","hoby"=>"AA"},
{"id"=>"3","hoby"=>"BB"},
{"id"=>"1","hoby"=>"CC"},
{"id"=>"1","hoby"=>"DD"},
{"id"=>"4","hoby"=>"EE"}
],
[ {"id"=>"1","language"=>"A"},
{"id"=>"1","language"=>"B"},
{"id"=>"2","language"=>"B"},
{"id"=>"2","language"=>"C"},
{"id"=>"6","language"=>"D"}
]
]
I need array output like,
output = [
{"id"=>"1","name"=>"a","id"=>"1","hoby"=>"CC","id"=>"1","language"=>"A","id"=>"1","language"=>"B"},
{"id"=>"2","name"=>"b","id"=>"2","language"=>"B"},
{"id"=>"3","name"=>"c","id"=>"3","hoby"=>"AA","id"=>"3","hoby"=>"BB"},
{"id"=>"4","name"=>"d","id"=>"4","hoby"=>"EE"},
{"id"=>"5","name"=>"e"},
{"id"=>"6","name"=>"f","id"=>"6","language"=>"D"}
]
I have wrote code for this,
len = input.length - 1
output = []
input[0].each do |value,index|
for i in 1..len
input[i].each do |j|
if value["id"] == j["id"]
output << value.merge(j)
end
end
end
end
But i am getting wrong output array.There might be any number of sub array in multidimensional array.
Thank,
First of all - it is impossible to have two elements in a hash with the same key. When assigning the value to some key will make the next assignment of the same key with new value override the previous one.
Let's consider the example:
hash = {}
hash["id"] = 1
hash["id"] = 3
hash["id"] = 5
What output for hash["id"] would you expect? 1, 3, 5 or maybe [1, 3, 5]? The way the Hash in ruby works it will output 5, because this is the last assignment to unique key.
Having said that, it is impossible to store multiple occurrences in your hash, but you can try processing it with something like:
input.flatten
.group_by { |h| h["id"] }
.map do |k, a|
a.each_with_object({}) { |in_h, out_h| out_h.merge!(in_h) }
end
Which will result with hash like:
[{"id"=>"1", "name"=>"a", "hoby"=>"DD", "language"=>"B"},
{"id"=>"2", "name"=>"b", "language"=>"C"},
{"id"=>"3", "name"=>"c", "hoby"=>"BB"},
{"id"=>"4", "name"=>"d", "hoby"=>"EE"},
{"id"=>"5", "name"=>"e"},
{"id"=>"6", "name"=>"f", "language"=>"D"}]
Well, it is not the hash as you would expect, but at least it might put you in some direction.
Hope that helps!
maybe this can help you.
input = [
[
{"id"=>"1","name"=>"a"},
{"id"=>"2","name"=>"b"},
{"id"=>"3","name"=>"c"},
{"id"=>"4","name"=>"d"},
{"id"=>"5","name"=>"e"},
{"id"=>"6","name"=>"f"}
],
[
{"id"=>"3","hoby"=>"AA"},
{"id"=>"3","hoby"=>"BB"},
{"id"=>"1","hoby"=>"CC"},
{"id"=>"1","hoby"=>"DD"},
{"id"=>"4","hoby"=>"EE"}
],
[
{"id"=>"1","language"=>"A"},
{"id"=>"1","language"=>"B"},
{"id"=>"2","language"=>"B"},
{"id"=>"2","language"=>"C"},
{"id"=>"6","language"=>"D"}
]
]
This way you can make your "sort" results.
output = {}
input.flatten.each do |h|
output[h["id"]] = {} unless output[h["id"]]
output[h["id"]].merge!(h)
end
output.values
# => [
# => {"id"=>"1", "name"=>"a", "hoby"=>"DD", "language"=>"B"},
# => {"id"=>"2", "name"=>"b", "language"=>"C"},
# => {"id"=>"3", "name"=>"c", "hoby"=>"BB"},
# => {"id"=>"4", "name"=>"d", "hoby"=>"EE"},
# => {"id"=>"5", "name"=>"e"},
# => {"id"=>"6", "name"=>"f", "language"=>"D"}
# => ]
But the better way is use Hash in input. You can define input like hash and "id" like key so if you generate the data, you dont have problem to sort it.
Someting like this
{
"1" => {"name" => "a", "hoby" => "DD", "language" => "B"}
}

Sort specific items of an array first

I have a ruby array that looks something like this:
my_array = ['mushroom', 'beef', 'fish', 'chicken', 'tofu', 'lamb']
I want to sort the array so that 'chicken' and 'beef' are the first two items, then the remaining items are sorted alphabetically. How would I go about doing this?
irb> my_array.sort_by { |e| [ e == 'chicken' ? 0 : e == 'beef' ? 1 : 2, e ] }
#=> ["chicken", "beef", "fish", "lamb", "mushroom", "tofu"]
This will create a sorting key for each element of the array, and then sort the array elements by their sorting keys. Since the sorting key is an array, it compares by position, so [0, 'chicken'] < [1, 'beef'] < [2, 'apple' ] < [2, 'banana'].
If you don't know what elements you wanted sorted to the front until runtime, you can still use this trick:
irb> promotables = [ 'chicken', 'beef' ]
#=> [ 'chicken', 'beef' ]
irb> my_array.sort_by { |e| [ promotables.index(e) || promotables.size, e ] }
#=> ["chicken", "beef", "fish", "lamb", "mushroom", "tofu"]
irb> promotables = [ 'tofu', 'mushroom' ]
#=> [ 'tofu', 'mushroom' ]
irb> my_array.sort_by { |e| [ promotables.index(e) || promotables.size, e ] }
#=> [ "tofu", "mushroom", "beef", "chicken", "fish", "lamb"]
Mine's a lot more generic and more useful if you get your data only at runtime.
my_array = ['mushroom', 'beef', 'fish', 'chicken', 'tofu', 'lamb']
starters = ['chicken', 'beef']
starters + (my_array.sort - starters)
# => ["chicken", "beef" "fish", "lamb", "mushroom", "tofu"]
Could just do
firsts = ["chicken", "beef"]
[*firsts, *(my_array.sort - firsts)]
#=> ["chicken", "beef", "fish", "lamb", "mushroom", "tofu"]

Resources