Remove key/value pair from array of hashes - ruby

Suppose I have an array of hashes
a = [
{'id'=>'1','imageUrl'=>'abc','name'=>'x','age'=>'20'},
{'id'=>'2','imageUrl'=>'efg','name'=>'y','age'=>'30'},
{'id'=>'3','imageUrl'=>'hij','name'=>'z','age'=>'40'}
]
What can be the fastest way to remove the key 'name' and 'age' and their corresponding value from all hashes in the array?
Basically how can I remove multiple key/value pairs?

Try the following code:
a = [
{'id'=>'1','imageUrl'=>'abc','name'=>'x'},
{'id'=>'2','imageUrl'=>'efg','name'=>'y'},
{'id'=>'3','imageUrl'=>'hij','name'=>'z'}
]
a.each { |h| h.delete("name") }
p a # => [{"id"=>"1", "imageUrl"=>"abc"}, {"id"=>"2", "imageUrl"=>"efg"}, {"id"=>"3", "imageUrl"=>"hij"}]

Nothing like benchmarking:
Collected from the above answers: and using benchmark-ips
require 'benchmark/ips'
def a
[
{'id'=>'1','imageUrl'=>'abc','name'=>'x'},
{'id'=>'2','imageUrl'=>'efg','name'=>'y'},
{'id'=>'3','imageUrl'=>'hij','name'=>'z'}
]
end
Benchmark.ips do |x|
x.report("map w/ except!") do |times|
a.map {|o| o.except!('name') }
end
x.report("each w/ except!") do |times|
a.each {|o| o.except!('name') }
end
x.report("map w/ except") do |times|
a.map {|o| o.except('name') }
end
x.report("each w/ except") do |times|
a.each {|o| o.except('name') }
end
x.report("map w/ delete") do |times|
a.map { |h| h.delete("name") }
end
x.report("each w/ delete") do |times|
a.each { |h| h.delete("name") }
end
x.compare!
end
I got the following:
Calculating -------------------------------------
map w/ except! 8.438k i/100ms
each w/ except! 8.439k i/100ms
map w/ except 5.242k i/100ms
each w/ except 5.469k i/100ms
map w/ delete 9.840k i/100ms
each w/ delete 9.810k i/100ms
-------------------------------------------------
map w/ except! 1.311B (±25.3%) i/s - 2.994B
each w/ except! 1.360B (±25.2%) i/s - 3.048B
map w/ except 423.818M (±25.8%) i/s - 1.238B
each w/ except 458.859M (±25.7%) i/s - 1.315B
map w/ delete 1.955B (±24.0%) i/s - 3.982B
each w/ delete 2.025B (±23.5%) i/s - 4.033B
Comparison:
each w/ delete: 2024710811.4 i/s
map w/ delete: 1955349074.3 i/s - 1.04x slower
each w/ except!: 1360241861.3 i/s - 1.49x slower
map w/ except!: 1311373772.5 i/s - 1.54x slower
each w/ except: 458859254.7 i/s - 4.41x slower
map w/ except: 423818242.2 i/s - 4.78x slower
Using a.each { |h| h.delete("name") } is the fastest (as pointed in the comment).

For example use except! (or except) method:
a.map {|o| o.except!('name') }

Iterate the Array and delete it from each hash:
a = [
{'id'=>'1','imageUrl'=>'abc','name'=>'x'},
{'id'=>'2','imageUrl'=>'efg','name'=>'y'},
{'id'=>'3','imageUrl'=>'hij','name'=>'z'}
]
=> [{"id"=>"1", "imageUrl"=>"abc", "name"=>"x"}, {"id"=>"2", "imageUrl"=>"efg", "name"=>"y"}, {"id"=>"3", "imageUrl"=>"hij", "name"=>"z"}]
a.each do |h|
h.delete("name")
end
=> [{"id"=>"1", "imageUrl"=>"abc"}, {"id"=>"2", "imageUrl"=>"efg"}, {"id"=>"3", "imageUrl"=>"hij"}]

Related

Find element dict based on elements of a list

I have a dict similar to:
{"services"=>["foo", "bar", "test"], "foo_limit_max"=>2, "test_limit_min"=>6}
I want to extract from the list of services all of those services that have a key following servicename_limit_*
I tried the following dict['services'].select { |x| dict.key?(/#{x}_limit_*/)} but it's not returning anything.
Expected output would be ['foo', 'test']
I would go about it like this
dict['services'] & dict.keys.flat_map {|k| k.scan(/\w+(?=_limit_)/)}
Here we use Array#& (intersection) between the dict['services'] and the keys that match the given pattern.
dict.keys.flat_map {|k| k.scan(/\w+(?=_limit_)/)}
#=> ["foo", "test"]
Another as suggested by #SergioTulentsev (which is far more performant) is
dict['services'].select {|s| dict.any? {|k,_| k.start_with?(s)}}
Benchmarks Provided By #SergioTulentsev
# >> Warming up --------------------------------------
# >> sergio 54.608k i/100ms
# >> aleksei 6.257k i/100ms
# >> cary 40.951k i/100ms
# >> engineersmnky 16.093k i/100ms
# >> Calculating -------------------------------------
# >> sergio 663.692k (± 3.5%) i/s - 3.331M in 5.025472s
# >> aleksei 64.006k (± 2.9%) i/s - 325.364k in 5.087717s
# >> cary 472.838k (± 3.1%) i/s - 2.375M in 5.028133s
# >> engineersmnky 167.907k (± 2.0%) i/s - 852.929k in 5.081762s
# >>
# >> Comparison:
# >> sergio: 663691.8 i/s
# >> cary: 472838.2 i/s - 1.40x slower
# >> engineersmnky: 167906.6 i/s - 3.95x slower
# >> aleksei: 64005.5 i/s - 10.37x slower
# >>
input['services'].reject do |x|
input.keys.grep(/\A#{x}_limit_/).empty?
end
#⇒ ["foo", "test"]
h = {"services"=>["foo", "bar", "test"], "foo_limit_max"=>2, "test_limit_min"=>6}
h["services"] & h.keys.map { |k| k[/.+?(?=_limit_)/] }
#=> ["foo", "test"]
Note:
h.keys.map { |k| k[/.+?(?=_limit_)/] }
#=> [nil, "foo", "test"]
See Array#&, which includes the sentence, "The order is preserved from the original array.".

ruby performance - is &: slower than just calling method?

Out of following 2 options which is faster?
something {|i| i.foo }
something(&:foo)
I read somewhere that something(&:foo) is transformed into something {|i| i.send(:foo) }. Is it true?
If that is true, the question becomes - which is faster? i.foo or i.send(:foo)?
Any any case, which one should be used for better performance?
In ruby it is quite easy to write such a (micro) benchmark:
require 'benchmark/ips'
VALUES = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Benchmark.ips do |x|
x.compare!
x.report('symbol') do
VALUES.each(&:odd?)
end
x.report('method') do
VALUES.each do |value|
value.odd?
end
end
x.report('send') do
VALUES.each do |value|
value.send(:odd?)
end
end
end
(latest version is https://github.com/pascalbetz/benchmarks/blob/master/symbol_vs_proc.rb)
Ruby 1.9.3
symbol: 1349424.7 i/s
method: 1049894.7 i/s - 1.29x slower
send: 929240.4 i/s - 1.45x slower
Ruby 2.2.2
symbol: 1358391.5 i/s
method: 1146686.4 i/s - 1.18x slower
send: 820392.7 i/s - 1.66x slower
JRuby 9.0.5.0
symbol: 2959040.8 i/s
method: 2016911.1 i/s - 1.47x slower
send: 1305816.9 i/s - 2.27x slower
This is a different result as what Sawa explained in his answer.

slice word with previous character

Is there any better way to slice a word in ruby?
My solution:
str="TAMILNADU"
pairs=[]
numPairs= str.length-1
i=0
while i<numPairs do
pairs[i] = str.slice(i,2)
i+=1
end
Result
["TA", "AM", "MI", "IL", "LN", "NA", "AD", "DU"]
Yes there is:
irb(main):001:0> "TAMILNADU".chars.each_cons(2).map(&:join)
=> ["TA", "AM", "MI", "IL", "LN", "NA", "AD", "DU"]
See the documentation of Enumerable#each_cons.
And another one using a positive lookahead:
str = 'TAMILNADU'
str.scan(/(.)(?=(.))/).map(&:join)
#=> ["TA", "AM", "MI", "IL", "LN", "NA", "AD", "DU"]
Another version:
str = "TAMILNADU"
(0...str.size-1).map{|x| str[x,2]}
# => ["TA", "AM", "MI", "IL", "LN", "NA", "AD", "DU"]
Thanks(#Doorknob,#hirolau,#Stefan,#Cary Swoveland) for all of your responses find the benchmark report below.
Updated (Tue - Mar- 2015)
require 'benchmark/ips'
Benchmark.ips do |x|
str = "TAMILNADU"
x.report("1") {
pairs=[]
numPairs= str.length()-1
i=0
while i<numPairs do
pairs[i] = str.slice(i,2)
i+=1
end
}
x.report("2") {
str.chars.each_cons(2).map(&:join)
}
x.report("3") {
(0...str.size-1).map{|x| str[x,2]}
}
x.report("4"){
str.scan(/(.)(?=(.))/).map(&:join)
}
x.report("5"){
str.gsub(/(.)/,'\1\1')[1...-1].scan(/../)
}
x.report("6"){
str.gsub(/./) { |c| c*2 }[1...-1].scan(/../)
}
x.compare!
end
Calculating -------------------------------------
1 37.355k i/100ms
2 9.703k i/100ms
3 26.961k i/100ms
4 7.950k i/100ms
5 6.302k i/100ms
6 7.804k i/100ms
-------------------------------------------------
1 508.411k (±11.1%) i/s - 2.503M
2 107.568k (± 5.1%) i/s - 543.368k
3 332.923k (±10.2%) i/s - 1.672M
4 88.410k (± 4.5%) i/s - 445.200k
5 67.694k (± 4.8%) i/s - 340.308k
6 85.000k (± 5.8%) i/s - 429.220k
Comparison:
1: 508410.6 i/s
3: 332923.4 i/s - 1.53x slower
2: 107567.7 i/s - 4.73x slower
4: 88409.9 i/s - 5.75x slower
6: 85000.2 i/s - 5.98x slower
5: 67694.2 i/s - 7.51x slower
Another way using a regex:
str.gsub(/(.)/,'\1\1')[1...-1].scan(/../)
#=> ["TA", "AM", "MI", "IL", "LN", "NA", "AD", "DU"]
or a variant:
str.gsub(/./) { |c| c*2 }[1...-1].scan(/../)
Any another:
a = []
e = str.each_char
loop do
a << (e.next << e.peek)
end
a
Edit: I added the last method after the benchmarking was done, so I ran the benchmark myself. It was dead last. (I like it anyway.)

All combinations for hash of arrays [duplicate]

This question already has answers here:
Turning a Hash of Arrays into an Array of Hashes in Ruby
(7 answers)
Closed 9 years ago.
Summary
Given a Hash where some of the values are arrays, how can I get an array of hashes for all possible combinations?
Test Case
options = { a:[1,2], b:[3,4], c:5 }
p options.self_product
#=> [{:a=>1, :b=>3, :c=>5},
#=> {:a=>1, :b=>4, :c=>5},
#=> {:a=>2, :b=>3, :c=>5},
#=> {:a=>2, :b=>4, :c=>5}]
When the value for a particular key is not an array, it should simply be included as-is in each resulting hash, the same as if it were wrapped in an array.
Motivation
I need to generate test data given a variety of values for different options. While I can use [1,2].product([3,4],[5]) to get the Cartesian Product of all possible values, I'd rather use hashes to be able to label both my input and output so that the code is more self-explanatory than just using array indices.
I suggest a little pre-processing to keep the result general:
options = { a:[1,2], b:[3,4], c:5 }
options.each_key {|k| options[k] = [options[k]] unless options[k].is_a? Array}
=> {:a=>[1, 2], :b=>[3, 4], :c=>[5]}
I edited to make a few refinements, principally the use of inject({}):
class Hash
def self_product
f, *r = map {|k,v| [k].product(v).map {|e| Hash[*e]}}
f.product(*r).map {|a| a.inject({}) {|h,e| e.each {|k,v| h[k]=v}; h}}
end
end
...though I prefer #Phrogz's '2nd attempt', which, with pre-processing 5=>[5], would be:
class Hash
def self_product
f, *r = map {|k,v| [k].product(v)}
f.product(*r).map {|a| Hash[*a.flatten]}
end
end
First attempt:
class Hash
#=> Given a hash of arrays get an array of hashes
#=> For example, `{ a:[1,2], b:[3,4], c:5 }.self_product` yields
#=> [ {a:1,b:3,c:5}, {a:1,b:4,c:5}, {a:2,b:3,c:5}, {a:2,b:4,c:5} ]
def self_product
# Convert array values into single key/value hashes
all = map{|k,v| [k].product(v.is_a?(Array) ? v : [v]).map{|k,v| {k=>v} }}
#=> [[{:a=>1}, {:a=>2}], [{:b=>3}, {:b=>4}], [{:c=>5}]]
# Create the product of all mini hashes, and merge them into a single hash
all.first.product(*all[1..-1]).map{ |a| a.inject(&:merge) }
end
end
p({ a:[1,2], b:[3,4], c:5 }.self_product)
#=> [{:a=>1, :b=>3, :c=>5},
#=> {:a=>1, :b=>4, :c=>5},
#=> {:a=>2, :b=>3, :c=>5},
#=> {:a=>2, :b=>4, :c=>5}]
Second attempt, inspired by #Cary's answer:
class Hash
def self_product
first, *rest = map{ |k,v| [k].product(v.is_a?(Array) ? v : [v]) }
first.product(*rest).map{ |x| Hash[x] }
end
end
In addition to being more elegant, the second answer is also about 4.5x faster than the first when creating a large result (262k hashes with 6 keys each):
require 'benchmark'
Benchmark.bm do |x|
n = *1..8
h = { a:n, b:n, c:n, d:n, e:n, f:n }
%w[phrogz1 phrogz2].each{ |n| x.report(n){ h.send(n) } }
end
#=> user system total real
#=> phrogz1 4.450000 0.050000 4.500000 ( 4.502511)
#=> phrogz2 0.940000 0.050000 0.990000 ( 0.980424)

Changing every value in a hash in Ruby

I want to change every value in a hash so as to add '%' before and after the value so
{ :a=>'a' , :b=>'b' }
must be changed to
{ :a=>'%a%' , :b=>'%b%' }
What's the best way to do this?
In Ruby 2.1 and higher you can do
{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
If you want the actual strings themselves to mutate in place (possibly and desirably affecting other references to the same string objects):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |_,str| str.gsub! /^|$/, '%' }
my_hash.each{ |_,str| str.replace "%#{str}%" }
If you want the hash to change in place, but you don't want to affect the strings (you want it to get new strings):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |key,str| my_hash[key] = "%#{str}%" }
my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
If you want a new hash:
# Ruby 1.8.6+
new_hash = Hash[*my_hash.map{|k,str| [k,"%#{str}%"] }.flatten]
# Ruby 1.8.7+
new_hash = Hash[my_hash.map{|k,str| [k,"%#{str}%"] } ]
Ruby 2.4 introduced the method Hash#transform_values!, which you could use.
{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"}
The best way to modify a Hash's values in place is
hash.update(hash){ |_,v| "%#{v}%" }
Less code and clear intent. Also faster because no new objects are allocated beyond the values that must be changed.
A bit more readable one, map it to an array of single-element hashes and reduce that with merge
the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
There is a new 'Rails way' method for this task :)
http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
One method that doesn't introduce side-effects to the original:
h = {:a => 'a', :b => 'b'}
h2 = Hash[h.map {|k,v| [k, '%' + v + '%']}]
Hash#map may also be an interesting read as it explains why the Hash.map doesn't return a Hash (which is why the resultant Array of [key,value] pairs is converted into a new Hash) and provides alternative approaches to the same general pattern.
Happy coding.
[Disclaimer: I am not sure if Hash.map semantics change in Ruby 2.x]
my_hash.each do |key, value|
my_hash[key] = "%#{value}%"
end
Hash.merge! is the cleanest solution
o = { a: 'a', b: 'b' }
o.merge!(o) { |key, value| "%#{ value }%" }
puts o.inspect
> { :a => "%a%", :b => "%b%" }
After testing it with RSpec like this:
describe Hash do
describe :map_values do
it 'should map the values' do
expect({:a => 2, :b => 3}.map_values { |x| x ** 2 }).to eq({:a => 4, :b => 9})
end
end
end
You could implement Hash#map_values as follows:
class Hash
def map_values
Hash[map { |k, v| [k, yield(v)] }]
end
end
The function then can be used like this:
{:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
# {:a=>"%a%", :b=>"%b%"}
If you are curious which inplace variant is the fastest here it is:
Calculating -------------------------------------
inplace transform_values! 1.265k (± 0.7%) i/s - 6.426k in 5.080305s
inplace update 1.300k (± 2.7%) i/s - 6.579k in 5.065925s
inplace map reduce 281.367 (± 1.1%) i/s - 1.431k in 5.086477s
inplace merge! 1.305k (± 0.4%) i/s - 6.630k in 5.080751s
inplace each 1.073k (± 0.7%) i/s - 5.457k in 5.084044s
inplace inject 697.178 (± 0.9%) i/s - 3.519k in 5.047857s

Resources