remove the key and value in the params hash with ruby on rails - ruby-on-rails

hello how is to remove empty value or nil in ruby on rails
I try to reject the method but it does not work there are there any other method?
Here is my hash with empty values
{
first_name: {
1: "david",
2: ""
},
last_name: {
1: "david",
2: ""
},
role: {
1: "dev",
2: ""
},
bio: {
1: "commercial",
2: ""
},
thank you

hash.each {|_, v| v.delete_if {|_, v| v == ""}}
#=> {"first_name"=>{1=>"david"}, "last_name"=>{1=>"david"}, "role"=>{1=>"dev"}, "bio"=>{1=>"commercial"}}

class Hash
def compact(opts={})
inject({}) do |new_hash, (k,v)|
if !v.blank?
new_hash[k] = opts[:recursive] && v.class == Hash ? v.compact(opts) : v
end
new_hash
end
end
end
hash = {
:first_name=> {
1=> "david",
2=> ""
},
:last_name=> {
1=> "david",
2=> ""
},
:role=> {
1=> "dev",
2=> ""
},
:bio=> {
1=> "commercial",
2=> ""
}
}
hash.compact(:recursive=>true)
will give
{
:first_name => {
1 => "david"
},
:last_name => {
1 => "david"
},
:role => {
1 => "dev"
},
:bio => {
1 => "commercial"
}
}
source: Removing all empty elements from a hash / YAML?

Related

Iterate over hash and return newly formed hash

I'm trying to iterate over hash and return new hash. My original hash is:
companies = {
company_id: {
"0": { title: "Google", address: "New str" },
"1": { title: "Facebook", address: "Old str." },
"2": { title: "Amazon", address: "River str." }
}
}
I want to return hash that is structured this way:
{
title: "Google",
address: "New str."
}
If company_id equal to "0" I would need to return details of that company, similar to below:
companies.each do |k,v|
v.each do |k,v|
if k.to_s == "0"
title: v[:title]
address: v[:address]
end
end
end
Iteration above doesn't return me hash, how can I get structured hash that I need? Thanks.
Simply do
companies[:company_id][:"0"]
# { title: "Google", address: "New str." }

Merge a single array into nested array in rails

I have a list of objects something like this:
a = [
{
id: 0,
name: "ABC",
work: "ABC2"
},
{
id: 0,
name: "XYZ",
work: "XYZ2"
},
{
id: 1,
name: "ACD",
work: "ACD2"
}
]
And I want to convert it into something like this:
b = [
{
id: 0,
fields: [
{
name: "ABC",
work: "ABC2"
},
{
name: "XYZ",
work: "XYZ2"
}
]
},
{
id: 1,
fields: [
{
name: "ACD",
work: "ACD2"
}
]
}
]
The idea is to group the objects (by id) in one array.
The approach I tried is:
b = []
rest_object = []
a.each_with_index do |aa, idx|
aa.delete(:id)
rest_object << aa
if idx == 0
next
end
puts a[idx][:id], a[idx-1][:id]
if a[idx][:id] != a[idx-1][:id]
b << {id: a[idx-1][:id], names: rest_object}
rest_object = []
end
end
But I am getting an empty output.
Also, if it is possible to achieve the same in some cleaner way.
That would be helpful.
Something like this does the job. This deletes the :id key-value pair from each hash and uses the value to group the remainder of the hash. Then map the resulting hash to created an array and transform the data into {id: ..., fields: ...} format.
a = [{id: 0, name: "ABC", work: "ABC2"}, {id: 0, name: "XYZ", work: "XYZ2"}, {id: 1, name: "ACD", work: "ACD2"}]
b = a.group_by { |hash| hash.delete(:id) }
.map { |id, fields| {id: id, fields: fields} }
#=> [{:id=>0, :fields=>[{:name=>"ABC", :work=>"ABC2"}, {:name=>"XYZ", :work=>"XYZ2"}]}, {:id=>1, :fields=>[{:name=>"ACD", :work=>"ACD2"}]}]
Note: This mutates the hashes in the a array. If you don't want this change a.group_by to a.map(&:dup).group_by. Which first duplicates all hashes before doing any mutations.
Try following,
required_keys = a[0].except(:id).keys
b = a.group_by { |x| x[:id] }
b = b.inject([]) do |m,(k,v)|
arr = { id: k }
required_keys.each do |key|
arr[key.to_s.pluralize.to_sym] = v.map { |z| z.slice(key) }
end
m << arr
end
# => [{:id=>0, :names=>[{:name=>"ABC"}, {:name=>"XYZ"}]}, {:id=>1, :names=>[{:name=>"ACD"}]}]
a = [
{
id: 0,
name: "ABC"
},
{
id: 0,
name: "XYZ"
},
{
id: 1,
name: "ACD"
}
]
ary = []
a.each do|val|
ary[val[:id]] = {id: val[:id]} unless ary[val[:id]]
ary[val[:id]][:names] = [] unless ary[val[:id]][:names]
ary[val[:id]][:names].push({name: val[:name]})
end
If I understand, given a more general array like this:
a = [ { id: 0, name: "ABC", etc: "01" },
{ id: 0, name: "XYZ", etc: "02" },
{ id: 1, name: "ACD", etc: "11" } ]
The first idea I came up with is doing something like this:
require 'active_support/inflector' # for using pluralize
res = a.group_by{ |h| h[:id] }.values.map do |ary|
h = Hash.new{ |h,k| h[k] = [] }
ary.each { |hh| hh.each { |k,v| h[k] << v } }
h[:id] = h[:id].first
h.transform_keys do |k|
unless k == :id
k.to_s.pluralize.to_sym
else
k
end
end
end
res #=> [{:id=>0, :names=>["ABC", "XYZ"], :etcs=>["01", "02"]}, {:id=>1, :names=>["ACD"], :etcs=>["11"]}]
This is not exactly the format you require, but to get that format, just change this line
ary.each { |hh| hh.each { |k,v| h[k] << v } }
to
ary.each { |hh| hh.each { |k,v| h[k] << { k => v } } }

Ruby merge hash value

I've written an API to organize my daily user data.
The origin format is like
"dau": {
"2017-05-02": 1,
"2017-05-04": 2,
"2017-05-05": 2,
}
"new_user": {
"2017-05-02": 1,
"2017-05-04": 0,
"2017-05-07": 0,
}
It's hard to display in HTML table row by row.
Therefore, I want the format to become this one.
However, I have no idea how to deal with.
info: {
"2017-05-02": {
dau: 1,
new_user: 1
},
"2017-05-04": {
dau: 2,
new_user: 0
},
"2017-05-05": {
dau: 2
},
"2017-05-07": {
new_user: 0
}
}
suppose you have data, then
(data['dau'].keys + data['new_user'].keys).uniq.map { |k| [k, { dau: data['dau'][k].to_i, new_user: data['new_user'][k].to_i } ] }.to_h
and if you don't want default/0 values then,
(data['dau'].keys + data['new_user'].keys).uniq.map { |k| [k, { dau: data['dau'][k], new_user: data['new_user'][k] }.compact ] }.to_h
Output:
{
"2017-05-02" => {
:dau => 1,
:new_user => 1
},
"2017-05-04" => {
:dau => 2,
:new_user => 0
},
"2017-05-05" => {
:dau => 2
},
"2017-05-07" => {
:new_user => 0
}
}
Hope it helps..
keys = (dau.keys + new_user.keys).uniq
# [:"2017-05-02", :"2017-05-04", :"2017-05-05", :"2017-05-07"]
keys.each_with_object({}) do |key, result|
result[key] = {dau: dau[key], new_user: new_user[key] }.compact
end
# {:"2017-05-02"=>{:dau=>1, :new_user=>1},
# :"2017-05-04"=>{:dau=>2, :new_user=>0},
# :"2017-05-05"=>{:dau=>2},
# :"2017-05-07"=>{:new_user=>0}}
Here's an alternative with each_with_object. It should work with any number of keys:
data = {"dau": {
"2017-05-02": 1,
"2017-05-04": 2,
"2017-05-05": 2
},
"new_user": {
"2017-05-02": 1,
"2017-05-04": 0,
"2017-05-07": 0
}
}
info = Hash.new { |h, k| h[k] = {} }
info = data.each_with_object(info) do |(key, sub_h), h|
sub_h.each do |date, i|
h[date][key] = i
end
end
p info
# {:"2017-05-02"=>{:dau=>1, :new_user=>1}, :"2017-05-04"=>{:dau=>2, :new_user=>0}, :"2017-05-05"=>{:dau=>2}, :"2017-05-07"=>{:new_user=>0}}

Ruby refactoring: converting array to hash

Here's what I get in Rails params:
obj => {
"raw_data" =>
[
{ "id" => "1", "name" => "John Doe" },
{ "id" => "2", "name" => "Jane Doe" }
]
}
I have to transform into a following object:
obj => {
"data" =>
{
"1" => { "name" => "John Doe" },
"2" => { "name" => "Jane Doe" }
}
}
Here's the code I have working so far:
if obj[:raw_data]
obj[:data] = Hash.new
obj[:raw_data].each do |raw|
obj[:data][raw[:id]] = Hash.new
obj[:data][raw[:id]][:name] = raw[:name] if raw[:name].present?
end
end
obj.delete(:raw_data)
Is there a way to refactor it? Maybe using map. Note that data structure has to change from array to hash as well.
Thanks for any tips.
Here's one way:
obj = {
"raw_data" => [
{ "id" => "1", "name" => "John Doe" },
{ "id" => "2", "name" => "Jane Doe" }
]
}
data = obj["raw_data"].map do |item|
item = item.dup
[ item.delete('id'), item ]
end
obj2 = { "data" => data.to_h }
# => { "data" =>
# { "1" => { "name" => "John Doe" },
# "2" => { "name" => "Jane Doe" }
# }
# }
If you're using Rails you can use the Hash#except method from ActiveSupport to make it a little more succinct:
data = obj["raw_data"].map {|item| [ item["id"], item.except("id") ] }
obj2 = { "data" => data.to_h }
d = obj[:raw_data]
keys = d.map { |h| h["id"] }
values = d.map { |h| h.except("id") }
Hash[ keys.zip(values) ]
# or as a oneliner
Hash[ d.map { |h| h["id"] }.zip(d.map { |h| h.except("id")) ]
# => {"1"=>{"name"=>"John Doe"}, "2"=>{"name"=>"Jane Doe"}}
This special Hash[] syntax lets you create a hash from a array of keys and an array of values.
Hash.except(*args) is an ActiveSupport addition to the hash class which returns a new key without the keys in the blacklist.
In rails, you can use index_by method:
obj = {raw_data: [{id: "1", name: "John Doe"}, {id: "2", name: "Jane Doe"}]}
obj2 = {
data: obj[:raw_data].index_by {|h| h[:id]}.each {|_,h| h.delete(:id)}
} #=> {:data=>{"1"=>{:name=>"John Doe"}, "2"=>{:name=>"Jane Doe"}}}
One downfall of this is that it will modify the original data by deleting id property. If this is unacceptable, here is modified, safe version:
obj2 = {
data: obj[:raw_data].map(&:clone).index_by {|h| h[:id]}.each {|_,h| h.delete(:id)}
} #=> {:data=>{"1"=>{:name=>"John Doe"}, "2"=>{:name=>"Jane Doe"}}}
I assume you mean obj = {...} and not obj => {...}, as the latter is not a valid object. If so:
{ "data" => obj["raw_data"].each_with_object({}) { |g,h|
h[g["id"]] = g.reject { |k,_| k == "id" } } }
#=> {"data"=>{"1"=>{"name"=>"John Doe"}, "2"=>{"name"=>"Jane Doe"}}}
If obj can be mutated, you can simplify a bit:
{ "data" => obj["raw_data"].each_with_object({}) { |g,h| h[g.delete("id")]=g } }
As an improved non-mutating solution, #Max suggested a Rails' tweak:
{ "data" => obj["raw_data"].each_with_object({}) { |g,h| h[g["id"]] = g.except("id") } }
That looks good to me, but as I don't know rails, I'm taking that advice at face value.

Hash remove all except specific keys

I would like to remove every key from a hash except a given key.
For example:
{
"firstName": "John",
"lastName": "Smith",
"age": 25,
"address":
{
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021"
},
"phoneNumber":
[
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
]
}
I want to remove everything except "firstName" and/or "address".
What about slice?
hash.slice('firstName', 'lastName')
# => { 'firstName' => 'John', 'lastName' => 'Smith' }
Available in Ruby since 2.5
Some other options:
h.select {|k,v| ["age", "address"].include?(k) }
Or you could do this:
class Hash
def select_keys(*args)
select {|k,v| args.include?(k) }
end
end
So you can now just say:
h.select_keys("age", "address")
If you use Rails, please consider ActiveSupport except() method: http://apidock.com/rails/Hash/except
hash = { a: true, b: false, c: nil}
hash.except!(:c) # => { a: true, b: false}
hash # => { a: true, b: false }
Hash#select does what you want:
h = { "a" => 100, "b" => 200, "c" => 300 }
h.select {|k,v| k > "a"} #=> {"b" => 200, "c" => 300}
h.select {|k,v| v < 200} #=> {"a" => 100}
Edit (for comment):
assuming h is your hash above:
h.select {|k,v| k == "age" || k == "address" }
hash = { a: true, b: false, c: nil }
hash.extract!(:c) # => { c: nil }
hash # => { a: true, b: false }
Inspired by Jake Dempsey's answer, this one should be faster for large hashes, as it only peaks explicit keys rather than iterating through the whole hash:
class Hash
def select_keys(*args)
filtered_hash = {}
args.each do |arg|
filtered_hash[arg] = self[arg] if self.has_key?(arg)
end
return filtered_hash
end
end
No Rails needed to get a very concise code:
keys = [ "firstName" , "address" ]
# keys = hash.keys - (hash.keys - keys) # uncomment if needed to preserve hash order
keys.zip(hash.values_at *keys).to_h

Resources