Classic hash to dot-notation hash - ruby-on-rails

Is there a simple way in Ruby 2/Rails 3 to transform this:
{a: {b: {"1" => 1, "2" => 2}, d: "Something"}, b: {c: 1}}
into this:
{"a.b.1" => 1, "a.b.2" => 2, "a.d" => "Something", "b.c" => 1}
I'm not talking about this precise hash but transform any hash into a dot-notation hash.

Here's the cleanest solution I could come up with right now:
def dot_it(object, prefix = nil)
if object.is_a? Hash
object.map do |key, value|
if prefix
dot_it value, "#{prefix}.#{key}"
else
dot_it value, "#{key}"
end
end.reduce(&:merge)
else
{prefix => object}
end
end
Test:
input = {a: {b: {"1" => 1, "2" => 2}, d: "Something"}, b: {c: 1}}
p dot_it input
Output:
{"a.b.1"=>1, "a.b.2"=>2, "a.d"=>"Something", "b.c"=>1}

Related

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 do I get the keys of a hash whose values equal to given arguments in ruby?

So I am trying to solve this problem from rubeque http://www.rubeque.com/problems/related-keys-of-hash/. Basicaly I just have to get keys of a hash whose values equal to given arguments.I was wondering if you guys can give me some hints? to solving this problem, thank you very much
this is what I have so far
class Hash
def keys_of(*args)
key = Array.new
args.each { |x| key << x}
key.each { |x,y| x if y == key}
end
end
assert_equal [:a], {a: 1, b: 2, c: 3}.keys_of(1)
assert_equal [:a, :d], {a: 1, b: 2, c: 3, d: 1}.keys_of(1)
assert_equal [:a, :b, :d], {a: 1, b: 2, c: 3, d: 1}.keys_of(1, 2)
Use Hash#select:
{a: 1, b: 2, c: 3, d: 1}.select { |key, value| value == 1 }
# => {:a=>1, :d=>1}
{a: 1, b: 2, c: 3, d: 1}.select { |key, value| value == 1 }.keys
# => [:a, :d]
{a: 1, b: 2, c: 3, d: 1}.select { |key, value| [1,2].include? value }.keys
#=> [:a, :b, :d]
class Hash
def keys_of(*args)
select { |key, value| args.include? value }.keys
end
end
h = {a: 1, b: 2, c: 3, d: 1}
p h.each_with_object([]){|(k,v),ar| ar<<k if [1,2].member?(v)}
# >> [:a, :b, :d]

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.

Is there an opposite function of slice function in Ruby?

In this post, slice function is used to get only necessary elements of params. What would be the function I should use to exclude an element of params (such as user_id)?
Article.new(params[:article].slice(:title, :body))
Thank you.
Use except:
a = {"foo" => 0, "bar" => 42, "baz" => 1024 }
a.except("foo")
# returns => {"bar" => 42, "baz" => 1024}
Inspired in the sourcecode of except in Rails' ActiveSupport
You can do the same without requiring active_support/core_ext/hash/except
# h.slice( * h.keys - [k1, k2...] )
# Example:
h = { a: 1, b: 2, c: 3, d: 4 }
h.slice( * h.keys - [:b, :c] ) # => { a: 1, d: 4}
Try this
params = { :title => "title", :other => "other", :body => "body" }
params.select {|k,v| [:title, :body].include? k } #=> {:title => "title", :body => "body"}
Considering only standard Ruby.
For Ruby versions below 3, no.
For Ruby 3, yes. You can use except.

Converting Ruby hashes to arrays

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]}

Resources