Ruby filtering array of hash - ruby

I have the following array of arguments that has a hash with the args keys and their relevant passed values:
args_array = [{:collection=>["abe", "<mus>", "hest"], :include_blank=>true, :required=>true}]
I need to filter this hash to just get only some keys (like :include_blank and :required) in this example, with their relevant values like so:
filtered_args_hash = {:include_blank=>true, :required=>true}
So the collection arg is excluded, and just the include_blank, and required in which I specified are the ones returned.
Update:
Here is a keys array of symbols in which I need to filter according to them:
filter_keys = %i(include_blank required)
How I can do that?

One way is to use Hash#keep_if:
args_array[0].dup.keep_if {|k| filter_keys.include? k}
# => {:include_blank=>true, :required=>true}
Note that .dup is here to prevent args_array from being modified, don't use it if you do want args_array to be updated.

The following should do solve your problem:
args_array.collect {|a| a if a[:include_blank] == true && a[:required=>true] == true }.compact
or
dup_list = args_array.dup
dup_list.delete_if {|a| !(a[:include_blank] == true && a[:required=>true] == true) }

args_array.map do |elem|
elem.select do |k, _|
%i(include_blank required).include? k
end
end

You tagged as Rails so you can use Hash#slice:
args_array[0].slice *filter_keys
# => {:include_blank=>true, :required=>true}

Related

How to remove duplicate pair values from given array in Ruby?

I want to remove a pair of 'duplicates' from an array of strings, where each element has the form R1,R2, with varying numbers. In my case, a duplicate would be R2,R1 because it has the same elements of R1,R2 but inverted.
Given:
a = ['R1,R2', 'R3,R4', 'R2,R1', 'R5,R6']
The resulting array should be like so:
a = ['R1,R2', 'R3,R4', 'R5,R6']
How could I remove the duplicates so I would have the following?
A solution with Set
require 'set'
a.uniq { |item| Set.new(item.split(",")) } # => ["R1,R2", "R3,R4", "R5,R6"]
Here is a working example :
array = ['R1,R2', 'R3,R4', 'R2,R1', 'R5,R6']
array.uniq { |a| a.split(',').sort }
try this,
def unique(array)
pure = Array.new
for i in array
flag = false
for j in pure
flag = true if (j.split(",").sort == i.split(",").sort)
end
pure << i unless flag
end
return pure
end
reference: https://www.rosettacode.org/wiki/Remove_duplicate_elements#Ruby
If the elements of your array are "pairs", they should maybe be actual pairs and not strings, like this:
pairs = [['R1', 'R2'], ['R3', 'R4'], ['R2', 'R1'], ['R5', 'R6']]
And, in fact, since order doesn't seem to matter, it looks like they really should be sets:
require 'set'
sets = [Set['R1', 'R2'], Set['R3', 'R4'], Set['R2', 'R1'], Set['R5', 'R6']]
If that is the case, then Array#uniq will simply work as expected:
sets.uniq
#=> [#<Set: {"R1", "R2"}>, #<Set: {"R3", "R4"}>, #<Set: {"R5", "R6"}>]
So, the best way would be to change the code that produces this value to return an array of two-element sets.
If that is not possible, then you should transform the value at your system boundary when it enters the system, something like this:
sets = a.map {|el| el.split(',') }.map(&Set.method(:new))

Combine the values of two hash keys in an array of hashes

I have an array of hashes:
a = [{"ID"=>"FOO", "Type"=>"Name"}, {"ID"=>"1234", "Type"=>"CID"}]
I'm trying to extract the hash where the Type=='CID', then combine the two values to result in CID=1234.
I can do this in multiple steps:
h = a.find{|x| x['Type']=='CID'}
# => {"ID"=>"1234", "Type"=>"CID"}
"#{h['Type']}=#{h['ID']}"
# => "CID=1234"
Is there a way to do this in a one liner?
a.find { |h| h["Type"] == "CID" }&.values_at("Type", "ID")&.join("=")
#=>"CID=1234"
a.find { |h| h["Type"] == "cat" }&.values_at("Type", "ID")&.join("=")
#=> nil
& is Ruby's safe navigation operator, which made it's debut in Ruby v2.3. I added it to cause nil to be returned if there is no match on h["Type"].
You can do it in one line using:
a.select{|x| x['Type']=='CID'}
.map{|x| "type=#{x['Type']},id=#{x['ID']}"}[0]
You may try this:
if we are not having multiple values of Type = "CID":
a.select{|x| x["Type"] == "CID"}.map{|x| x.values_at("Type", "ID")}.join("=")
if we have are having Type="CID"
a.detect{|x| x["Type"]=="CID"}.values_at("Type", "ID").join("=")
If we don't have Type="CID" in above array will throw an error, be cautious.
Need to work in all cases we need to do:
a.detect{|x| x["Type"]=="CID"}.values_at("Type", "ID").join("=") if a.detect{|x| x["Type"]=="CID"}

Check if array element data[i][j][k] exists

I need to find out if data[i][j][k] element exists, but I don't know if data[i] or data[i][j] not nil themselves.
If I just data[i][j][k].nil?, it throw undefined method [] for nil:NilClass if data[i] or data[i][j] is nil
So, I am using now
unless data[i].nil? or data[i][j].nil? or data[i][j][k].nil?
# do something with data[i][j][k]
end
But it is somehow cumbersome.
Is there any other way to check if data[i][j][k] exists without data[i].nil? or data[i][j].nil? or data[i][j][k].nil? ?
I usually do:
unless (data[i][j][k] rescue false)
# do something
end
Here are three different alternatives:
Shorten it
You can shorten it slightly by using "!" instead of .nil?:
!data[i] or !data[i][j] or !data[i][j][k]
You could get rid of the repetition by doing this:
((data[i] || [])[j] || [])[k].nil?
Abstract away these details
Both of the code snippets above are nasty enough that I would probably not write them more than once in a code base.
A three-dimensional array seems complicated enough that you shouldn't be accessing it directly in lots of places in your code. You should consider wrapping it inside an object with an appropriate name:
class My3DWorld
def initialize
# set up #data
end
# Gets the point or returns nil if it doesn't exist.
def get_point(i, j, k)
#data[i] && #data[i][j] && #data[i][j][k]
end
end
Use a hash instead
However, ultimately, I wonder whether you really need a 3D array. Another more Ruby-like way to implement this data structure would be to use a hash and use i,j,k coordinate tuples as the keys. Unless this is a huge structure and you need the performance characteristics of a 3D array, I recommend looking at my other answer here:
https://stackoverflow.com/a/20600345/28128
The new feature "refinements" is an option:
module ResponsiveNil
refine NilClass do
def [](obj)
nil
end
end
end
using ResponsiveNil
a = [[1]]
p a[2][3][4] #=> nil
You can shorten slightly to
if data[i] && data[i][j] && data[i][j][k]
# do something with data[i][j][k]
end
You can also you the "andand" gem which allows you to write:
data[i].andand[j].andand[k]
If you are willing to monkey patch Array, you could define a method to enable this, such as:
class Array
def index_series(*args)
result = self
args.each do |key|
result = result[key]
return nil if result.nil?
end
result
end
end
which would let you do:
data.index_series(i, j, k)
The following permits any amount of nesting, and allows for the possibility that an element of the array has a value of nil:
def element_exists?(arr, *indices)
if arr.is_a? Array
return true if indices.empty?
return false if arr.size <= (i = indices.pop)
element_exists?(arr[i], *indices)
else
indices.empty?
end
end
data = [[0,1],[2,nil]]
element_exists?(data) # => true
element_exists?(data, 1) # => true
element_exists?(data, 2) # => false
element_exists?(data, 1, 1) # => true
element_exists?(data, 1, 2) # => false
element_exists?(data, 1, 1, 1) # => false

This is wrong, but why (and how can I get it to flow better)?

So I am figuring out how to set up some options for a class. 'options' is a hash. I want to
1) filter out options I don't want or need
2) set some instance variables to use elsewhere
3) and set up another hash with the processed options as #current_options.
def initialize_options(options)
#whitelisted_options, #current_options = [:timestamps_offset, :destructive, :minimal_author], {}
n_options = options.select { |k,v| #whitelisted_options.include?(k) }
#current_options[:timestamps_offset] = #timestamp_offset = n_options.fetch(:timestamps_offset, 0)*(60*60*24)
#current_options[:destructive] = #destructive = n_options.fetch(:destructive, false)
#current_options[:minimal_author] = #minimal_author = n_options.fetch(:minimal_author, false)
end
I'm guessing this is a bit much, no matter what I pass in I get:
{:timestamps_offset=>0, :destructive=>false, :minimal_author=>false}
When I do this line by line from the command line, it works as I want it to but not in my class. So what is going on and how do I clean this up?
EDIT: this actually works disembodied from the class I'm using it in, but inside it doesn't so the reality is something is going on I'm not aware of right now.
attr_reader :current_options is how this is set on the class, perhaps that needs some revision.
EDIT2: line 2 of the method is supposed to select from #whitelisted_options
EDIT3: Actually turned out to be something I wasn't thinking of..."options" comes in parsed from a yaml file as strings....and I was fetching symbols, changing that around makes a difference where before the method was looking for symbols and finding none, e.g. "destructive" vs :destructive, so always defaulting to the defaults. In short, I just needed to symbolize the hash keys when options are imported.
Your #current_options is initialized as an empty hash. When you filter the options passed as params, none of the keys will be present in #current_options so n_options will end up empty.
Then when you set up #current_options in the following lines, it will always grab the default values (0, false, false), and that's why your output's always the same.
You solve this problem by conditionally initializing #current_options so that it's only set to {} once:
#current_options ||= {}
Post-OP edit:
Your issue's with options.select -- in Ruby 1.8, it doesn't return a Hash, but rather an Array. Your calls to fetch are then always failing (as symbols can't be array indexes), so always returning defaults.
Instead, try:
n_options = options.inject({}) {|h, p| h[p[0]] = p[1] if #whitelisted_options.include? p[0]; h }
where p is an array containing each key/value pair.
In Ruby 1.9.2, Hash.select behaves the way you expected it to.
Edit 2: Here's how I'd approach it:
class Foo
##whitelisted_options= {:timestamps_offset => 0, :destructive => false, :minimal_author =>false}
##whitelisted_options.keys.each do |option|
define_method(option) { return #current_options[option] rescue nil}
end
def initialize_options(options)
#current_options = {}
##whitelisted_options.each {|k, v| #current_options[k] = options[k] || v}
#current_options
end
end
In use:
f = Foo.new
f.destructive #=> nil
f.initialize_options(:minimal_author => true, :ignore => :lol)
f.destructive #=> false
f.minimal_author #=> true
f.timestamps_offset #=> 0
What is #whitelisted_options for?
What do you want to happen if :destructive is not a key in options? Do you want to have :destructive => false, or do you want #current_options to not mention :destructive at all?

Search ruby hash for empty value

I have a ruby hash like this
h = {"a" => "1", "b" => "", "c" => "2"}
Now I have a ruby function which evaluates this hash and returns true if it finds a key with an empty value. I have the following function which always returns true even if all keys in the hash are not empty
def hash_has_blank(hsh)
hsh.each do |k,v|
if v.empty?
return true
end
end
return false
end
What am I doing wrong here?
Try this:
def hash_has_blank hsh
hsh.values.any? &:empty?
end
Or:
def hash_has_blank hsh
hsh.values.any?{|i|i.empty?}
end
If you are using an old 1.8.x Ruby
I hope you're ready to learn some ruby magic here. I wouldn't define such a function globally like you did. If it's an operation on a hash, than it should be an instance method on the Hash class you can do it like this:
class Hash
def has_blank?
self.reject{|k,v| !v.nil? || v.length > 0}.size > 0
end
end
reject will return a new hash with all the empty strings, and than it will be checked how big this new hash is.
a possibly more efficient way (it shouldn't traverse the whole array):
class Hash
def has_blank?
self.values.any?{|v| v.nil? || v.length == 0}
end
end
But this will still traverse the whole hash, if there is no empty value
I've changed the empty? to !nil? || length >0 because I don't know how your empty method works.
If you just want to check if any of the values is an empty string you could do
h.has_value?('')
but your function seems to work fine.
I'd consider refactoring your model domain. Obviously the hash represents something tangible. Why not make it an object? If the item can be completely represented by a hash, you may wish to subclass Hash. If it's more complicated, the hash can be an attribute.
Secondly, the reason for which you are checking blanks can be named to better reflect your domain. You haven't told us the "why", but let's assume that your Item is only valid if it doesn't have any blank values.
class MyItem < Hash
def valid?
!invalid?
end
def invalid?
values.any?{|i| i.empty?}
end
end
The point is, if you can establish a vocabulary that makes sense in your domain, your code will be cleaner and more understandable. Using a Hash is just a means to an end and you'd be better off using more descriptive, domain-specific terms.
Using the example above, you'd be able to do:
my_item = MyItem["a" => "1", "b" => "", "c" => "2"]
my_item.valid? #=> false

Resources