Removing all empty elements from a hash / YAML?
You could add a compact method to Hash like this
class Hash
def compact
delete_if { |k, v| v.nil? }
end
end
or for a version that supports recursion
class Hash
def compact(opts={})
inject({}) do |new_hash, (k,v)|
if !v.nil?
new_hash[k] = opts[:recurse] && v.class == Hash ? v.compact(opts) : v
end
new_hash
end
end
end
Recursively removing `nil` and empty values from hash
Just another implementation, written for fun & practice.
- No monkey patching
- Works on Hashes and Arrays
- Can be used as a module function like:
DeepCompact.deep_compact(hash)
- Also a destructive target modifying variant:
DeepCompact.deep_compact!(hash)
- Can be used by extending on an existing object:
{ foo: nil }.extend(DeepCompact).deep_compact
- Can be used via refinements: adding
using DeepCompact
to a file/class will bringdeep_compact
anddeep_compact!
to Hashes and Arrays for all code in that file/class.
Here's the module:
module DeepCompact
[Hash, Array].each do |klass|
refine klass do
def deep_compact
DeepCompact.deep_compact(self)
end
def deep_compact!
DeepCompact.deep_compact!(self)
end
end
end
def self.extended(where)
where.instance_exec do
def deep_compact
DeepCompact.deep_compact(self)
end
def deep_compact!
DeepCompact.deep_compact!(self)
end
end
end
def deep_compact(obj)
case obj
when Hash
obj.each_with_object({}) do |(key, val), obj|
new_val = DeepCompact.deep_compact(val)
next if new_val.nil? || (new_val.respond_to?(:empty?) && new_val.empty?)
obj[key] = new_val
end
when Array
obj.each_with_object([]) do |val, obj|
new_val = DeepCompact.deep_compact(val)
next if new_val.nil? || (new_val.respond_to?(:empty?) && new_val.empty?)
obj << val
end
else
obj
end
end
module_function :deep_compact
def deep_compact!(obj)
case obj
when Hash
obj.delete_if do |_, val|
val.nil? || (val.respond_to?(:empty?) && val.empty?) || DeepCompact.deep_compact!(val)
end
obj.empty?
when Array
obj.delete_if do |val|
val.nil? || (val.respond_to?(:empty?) && val.empty?) || DeepCompact.deep_compact!(val)
end
obj.empty?
else
false
end
end
module_function :deep_compact!
end
And then some examples on how to use it:
hsh = {
'hello' => [
'world',
{ 'and' => nil }
],
'greetings' => nil,
'salutations' => {
'to' => { 'you' => true, 'him' => 'yes', 'her' => nil },
'but_not_to' => nil
}
}
puts "Original:"
pp hsh
puts
puts "Non-destructive module function:"
pp DeepCompact.deep_compact(hsh)
puts
hsh.extend(DeepCompact)
puts "Non-destructive after hash extended:"
pp hsh.deep_compact
puts
puts "Destructive refinement for array:"
array = [hsh]
using DeepCompact
array.deep_compact!
pp array
And the output:
Original:
{"hello"=>["world", {"and"=>nil}],
"greetings"=>nil,
"salutations"=>
{"to"=>{"you"=>true, "him"=>"yes", "her"=>nil}, "but_not_to"=>nil}}
Non-destructive module function:
{"hello"=>["world"], "salutations"=>{"to"=>{"you"=>true, "him"=>"yes"}}}
Non-destructive after hash extended:
{"hello"=>["world"], "salutations"=>{"to"=>{"you"=>true, "him"=>"yes"}}}
Destructive refinement for array:
[{"hello"=>["world"], "salutations"=>{"to"=>{"you"=>true, "him"=>"yes"}}}]
Or just use one of the multiple gems that provide this for you.
Removing empty values from an deeply nested Ruby hash
Try the below with recusrion:
def remove_blank(hash)
hash.each do |_, value|
if value.is_a? Hash
remove_blank(value)
elsif value.is_a?(Array) && value[0].is_a?(Hash)
remove_blank(value[0])
end
end.reject! {|_, value| value.nil? || (value.is_a?(Array) && value.reject(&:empty?).empty?) }
end
remove the key and value in the params hash with ruby on rails
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?
How do I recursively flatten a YAML file into a JSON object where keys are dot separated strings?
Since the question is about using YAML files for i18n on a Rails app, it's worth noting that the i18n
gem provides a helper module I18n::Backend::Flatten
that flattens translations exactly like this:
test.rb
:
require 'yaml'
require 'json'
require 'i18n'
yaml = YAML.load <<YML
en:
questions:
new: 'New Question'
other:
recent: 'Recent'
old: 'Old'
YML
include I18n::Backend::Flatten
puts JSON.pretty_generate flatten_translations(nil, yaml, nil, false)
Output:
$ ruby test.rb
{
"en.questions.new": "New Question",
"en.questions.other.recent": "Recent",
"en.questions.other.old": "Old"
}
Remove duplicate keys from a hash of hashes and arrays (and ensure any resulting empty hashs are also removed)
I think this does what you want:
#!/usr/bin/perl
use warnings;
use strict;
use feature qw/say/;
use JSON::XS; # Better than JSON; also see JSON::MaybeXS
my $j = <<EOJSON;
{
"foo": 1,
"bar": {
"foo": true,
"baz": false
},
"dog": "woof",
"cat": [ { "foo": 3 } ]
}
EOJSON
sub count_keys {
my ($j, $seen) = @_;
my $type = ref $j;
if ($type eq "ARRAY") {
count_keys($_, $seen) for @$j;
} elsif ($type eq "HASH") {
while (my ($key, $val) = each %$j) {
$seen->{$key}++;
count_keys($val, $seen) if ref $val;
}
}
return $seen;
}
sub remove_dups {
my ($j, $seen) = @_;
$seen //= count_keys($j, {});
my $type = ref $j;
if ($type eq "ARRAY") {
return [ map { remove_dups($_, $seen) } @$j ];
} elsif ($type eq "HASH") {
my %obj = %$j;
delete @obj{grep { $seen->{$_} > 1 } keys %obj};
while (my ($key, $val) = each %obj) {
$obj{$key} = remove_dups($val, $seen) if ref $val;
}
return \%obj;
} else {
return $j;
}
}
my $parsed = decode_json $j;
my $printer = JSON::XS->new->pretty->canonical;
say "Before:";
print $printer->encode($parsed);
say "After:";
my $dedup = remove_dups $parsed;
print $printer->encode($dedup);
produces
Before:
{
"bar" : {
"baz" : false,
"foo" : true
},
"cat" : [
{
"foo" : 3
}
],
"dog" : "woof",
"foo" : 1
}
After:
{
"bar" : {
"baz" : false
},
"cat" : [
{}
],
"dog" : "woof"
}
Edit for explanation:
The first time remove_dups
is called on a perl data structure representing a json value (Which doesn't have to be a json object), it calls count_keys
to recursively walk the structure and create a hash of all the keys and the number of times each one occurs. Then it again recursively walks the structure, returning a deep copy without keys that appeared more than once in the original.
This line is the real magic:
delete @obj{grep { $seen->{$_} > 1 } keys %obj};
It uses a hash slice to delete a bunch of keys all at once, with the grep bit returning a list of keys that appeared more than once. More information on slices.
How to iterate over deep nested hash without known depth in Ruby
So, you want to parse recursively until there are no more levels to parse into.
It’s super common in software and referred to as “recursion”. Have a search around to learn more about it - it’ll come up again and again in your journey. Welcome to ruby btw!
As for your actual current problem. Have a read of https://mrxpalmeiras.wordpress.com/2017/03/30/how-to-parse-a-nested-yaml-config-file-in-python-and-ruby/
But also, consider the i18n gem. See this answer https://stackoverflow.com/a/51216931/1777331 and the docs for the gem https://github.com/ruby-i18n/i18n This might fix your problem of handling internationalisation without you having to get into the details of handling yaml files.
All keys from hash with matching array values in Rails 4
Try this one
CHAR_MAP.keys.select { |k| (CHAR_MAP[k] - x).empty? }
How do I fetch multiple hash keys with a nested hash?
I presume you want to grab the values out in a tuple? You can make an array that contains whatever collection of values you want.
Try the following for name and city:
[imahash[:id][:name], imahash[:location][:city]]
=> ["Alma", "Freeport"]
Related Topics
Ruby Keyword Arguments of Method
How to Evaluate a Date Difference in Years, Months and Days (Ruby)
Determine the Class to Which a Method Belongs in Rails
How to Increment/Decrement a Character in Ruby for All Possible Values
Finding the Ip Address of a Domain
What's the Best Way to Implement Acls to a Rails Application
Gem Which Cannot Find Gem Despite It Being Installed
Error - "Gem Install Rails" - Libxml2 Is Missing
Why Require Mongo Gives Me Loaderror: No Such File to Load -- Openssl
Ruby: What Is the Order of Keys/Values Returned by Hash.Keys and Hash.Values Methods
How to Perform Vector Addition in Ruby
Simple Conversion of String to Utf-8 in Ruby 1.8
Can Someone Please Explain Class << Self to Me
Colorized Output Breaks Linewrapping with Readline
Exception_Notification for Delayed_Job