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] }
Related
i'm new to ruby and i want to instersect two arrays
validAccountTypes = [
'Asset' => 'Asset',
'Liability' => 'Liability',
'Equity' => 'Equity',
'Income' => 'Income',
'CostOfSales' => 'Cost of Sales',
'Expense' => 'Expenses',
'OtherIncome' => 'Other Income',
'OtherExpense' => 'Other Expenses',
]
types = [
'Asset',
'Other Income',
'Other Expenses',
]
I want a result of valid Accounts with keys base on array types. The output would be
[{"Asset"=>"Asset", "OtherIncome"=>"Other Income", "OtherExpense" => "Other Expenses"}]
Is it possible without a loop?
Here's a rewrite of your variables with a few changes:
Underscore variable names
valid_account_types is now a hash instead of an array containing a hash.
Some typos corrected so that the members of types match keys of valid_account_types.
valid_account_types = {
'Asset' => 'Asset',
'Liability' => 'Liability',
'Equity' => 'Equity',
'Income' => 'Income',
'CostOfSales' => 'Cost of Sales',
'Expense' => 'Expenses',
'OtherIncome' => 'Other Income',
'OtherExpenses' => 'Other Expenses',
}
types = [
'Asset',
'OtherIncome',
'OtherExpenses',
]
Given that setup, if you are using Rails, you can get the result you want using Hash#slice, like this:
> valid_account_types.slice(*types)
=> {"Asset"=>"Asset", "OtherIncome"=>"Other Income", "OtherExpenses"=>"Other Expenses"}
Note that Hash#slice does not exist in Ruby itself. If you want to do this in plain-old Ruby, you could check out the implementation in i18n:
class Hash
def slice(*keep_keys)
h = self.class.new
keep_keys.each { |key| h[key] = fetch(key) if has_key?(key) }
h
end
end
Your first array isn't an array, it's actually a hash (notice the surrounding braces, rather than brackets):
validAccountTypes = {
'Asset' => 'Asset',
'Liability' => 'Liability',
'Equity' => 'Equity',
'Income' => 'Income',
'CostOfSales' => 'Cost of Sales',
'Expense' => 'Expenses',
'OtherIncome' => 'Other Income',
'OtherExpense' => 'Other Expenses',
}
You can get the keys on the hash and intersect it with your array:
common_keys = validAccountTypes.keys & types
Then you can select only those keys:
# Hash#slice works in Rails:
validAccountTypes.slice(*common_keys)
# Otherwise use Hash#select:
validAccountTypes.select { |key, value| common_keys.include?(key) }
Note that in your example, your hash keys include 'OtherIncome' and 'OtherExpense', but your types array includes 'Other Income' and 'Other Expenses'. Your array values should match the keys from the hash.
I got two hashes hash_a and hash_b which are actually arrays but have hash inside them. Those hash have unique key.
hash_a = [
{:unique_key => 1, :data => 'data for A1'},
{:unique_key => 2, :data => 'data for A2'},
{:unique_key => 3, :data => 'data for A3'}
]
hash_b = [
{:unique_key => 1, :data => 'data for B1'},
{:unique_key => 2, :data => 'data for B2'},
{:unique_key => 4, :data => 'data for B4'},
{:unique_key => 5, :data => 'data for B5'}
]
Now I want to find out difference between hash_a and hash_b, such that I get the hash_c as array of new hashes present in hash_b.
I basically want hash_b - hash_a
So I want this output for hash_c, hash_c should be this:
[
{:unique_key => 1, :data => 'data for A1'},
{:unique_key => 2, :data => 'data for A2'},
{:unique_key => 3, :data => 'data for A3'},
{:unique_key => 4, :data => 'data for B4'},
{:unique_key => 5, :data => 'data for B5'}
]
I have tried something like this:
hash_c = hash_a
hash_b.each do |inner_bhash|
found = 0
hash_a.each do |inner_ahash|
if(inner_ahash[:unique_key] == inner_bhash[:unique_key])
found = 1
break
end
end
if(found==0)
hash_c.push(inner_bhash)
end
end
This is doing the trick, but I want a better way. Like hashmap or something, I don't know what.
In addition, I may want to see only the new entries, i.e.
[
{:unique_key => 4, :data => 'data for B4'},
{:unique_key => 5, :data => 'data for B5'}
]
I can do that in my code by replacing
hash_c = hash_a
with
hash_c = []
but how could I adapt this requirement in the same way?
With Hashes you can use merge to do what you want - so going through making each Array into a Hash you can do the following:
hash_b.group_by { |e| e[:unique_key] }.
merge(hash_a.group_by { |e| e[:unique_key] }).values.flatten
# => [{:unique_key=>1, :data=>"data for A1"},
# {:unique_key=>2, :data=>"data for A2"},
# {:unique_key=>4, :data=>"data for B4"},
# {:unique_key=>5, :data=>"data for B5"},
# {:unique_key=>3, :data=>"data for A3"}]
If you want to have just the entries of hash_b (which do not have a key in hash_a), given you already have the solution above - you can simply subtract hash_a from the result:
hash_b.group_by { |e| e[:unique_key] }.
merge(hash_a.group_by { |e| e[:unique_key] }).values.flatten - hash_a
# => [{:unique_key=>4, :data=>"data for B4"},
# {:unique_key=>5, :data=>"data for B5"}]
Another, more straight forward way is to filter out all elements of hash_b who have an entry in hash_a:
hash_b.select { |x| hash_a.none? { |y| x[:unique_key] == y[:unique_key] } }
# => [{:unique_key=>4, :data=>"data for B4"},
# {:unique_key=>5, :data=>"data for B5"}]
You can use the form of Array#uniq that takes a block.
(hash_a + hash_b).uniq { |h| h[:unique_key] }
#=> [{:unique_key=>1, :data=>"data for A1"}, {:unique_key=>2, :data=>"data for A2"},
# {:unique_key=>3, :data=>"data for A3"}, {:unique_key=>4, :data=>"data for B4"},
# {:unique_key=>5, :data=>"data for B5"}]
To quote the doc, "self is traversed in order, and the first occurrence is kept."
I am wondering how to merge these two arrays into one clean array in Ruby
Both arrays share one similar key:value pair. I am trying to merge information from these two separate arrays that have information for the same person. One array has his name. The other array has his job and age. Both arrays have an id matching to the same person.
An example of what I am trying to do
array1 = [ {:id => 1, :name => "Bob"}, {:id => 2, :name => "Tim"}]
array2 = [ {:id => 1, :job => "firefighter", :age => 25}, { :id => 2, :job => "accountant", :age => 30} ]
new_array = [ {:id=> 1, name => "Bob", :job => "firefighter", :age => 25}, { :id => 2, :name => "Tim", :job => "accountant", :age => 30} ]
You could do something like this:
new_array = array1.each_with_index.map { |x, i| x.merge array2[i] }
# => [{:id=>1, :name=>"Bob", :job=>"firefighter", :age=>25}, {:id=>2, :name=>"Tim", :job=>"accountant", :age=>30}]
If you want a solution that is not dependent on the order of the array, and instead uses the :id to match the hashes:
array1.map { |x| x.merge (array2.find { |h| h[:id] == x[:id] } || {}) }
If the two arrays contain the same :id values at the same locations:
array1.zip(array2).map { |g,h| g.merge(h) }
#=> [{:id=>1, :name=>"Bob", :job=>"firefighter", :age=>25},
# {:id=>2, :name=>"Tim", :job=>"accountant", :age=>30}]
or equivalently:
[array1, array2].transpose.map { |g,h| g.merge(h) }
If the two arrays contain the same :id values but not necessarily at the same locations:
(array1 + array2).group_by { |h| h[:id] }
.values
.map { |g,h| g.merge(h) }
or
array1.sort_by { |h| h[:id] }
.zip(array2.sort_by { |h| h[:id] } )
.map { |g,h| g.merge(h) }
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.
I have a Hash which is of the form
{:a => {"aa" => 11,"ab" => 12}, :b => {"ba" => 21,"bb" => 22}}
How do i convert it to the form {:a => [["aa",11],["ab",12]],:b=>[["ba",21],["bb",22]]}
If you want to modify the original hash you can do:
hash.each_pair { |key, value| hash[key] = value.to_a }
From the documentation for Hash#to_a
Converts hsh to a nested array of [
key, value ] arrays.
h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
h.to_a #=> [["c", 300], ["a", 100], ["d", 400]]
Here is another way to do this :
hsh = {:a => {"aa" => 11,"ab" => 12}, :b => {"ba" => 21,"bb" => 22}}
hsh.each{|k,v| hsh[k]=*v}
# => {:a=>[["aa", 11], ["ab", 12]], :b=>[["ba", 21], ["bb", 22]]}
hash.collect {|a, b| [a, hash[a].collect {|c,d| [c,d]}] }.collect {|e,f| [e => f]}