Confusion with `Array#each` in Ruby - ruby

Confusion with Array#each as below:
%w{ david black }.each {|str| str.capitalize }
#=> ["david", "black"]
The above code is cool,but how the below logic works,couldn't understand.
%w{ david black }.each(&:capitalize)
#=> ["david", "black"]

It's a very old trick, called Symbol#to_proc.
You can read more about it here: http://pragdave.pragprog.com/pragdave/2005/11/symbolto_proc.html
Basically, it's a shortcut for calling methods that take no args. Often used in map, for example.
%w[i can measure length of strings].map(&:length) # => [1, 3, 7, 6, 2, 7]

How about using map?
[1173]pry(main)> ["david","black"].map{|str| str.capitalize }
=> ["David","Black"]
[1173]pry(main)>

The notation of &:something does for every element of the array the method something.
This is usually used with map to change every entry of the array or to extract data from hashes.
[{:foo => :bar, :meh => :bar2}, {:foo => :one, :meh => :two}].map(&:foo)
=> [:bar, :one]

I don't get your example with .each, maybe you meant .map
When you pass in the &:method_name it's shorthand for doing it in the block. So for each item apply this method.

Related

Need Ruby method to convert an array of strings into a Hash

I need Ruby method to convert an array of strings into a Hash where each key is a string and each value is the 1-indexed index of the string in the original array.
hashify(%w(a b c))
# should return
{'a' => 1, 'b' => 2, 'c' => 3}
Even though I think I'm helping someone do their homework, I can't resist taking a golf swing, because Ruby is awesome:
%w(a b c).each.with_index(1).to_h
Also, defining "hashify" is VERY un-Ruby-like. I'd suggest alternatives, but it's probably a homework assignment anyways and you don't seem to want to learn it.
def hashify(array)
array.each.with_index(1).to_h
end
hashify(%w(a b c))
#=> { "a" => 1, "b" => 2, "c" => 3 }
There are (clearly) multiple ways you could achieve your goal in Ruby.
If you consider the expression %w(a b c).map.with_index(1).to_h, you can see that it is a matter of stringing together a few methods provided to us by the Enumerable class.
By first sending the :map message to the array, we receive back an Enumerator, which provides us the handy :with_index method. As you can see in the docs, with_index accepts an offset in an argument, so offsetting your indices by 1 is as simple as passing 1 as your argument to :with_index.
Finally, we call :to_h on the enumerator to receive the desired hash.
# fore!
def hashify(array)
array.map.with_index(1).to_h
end
> hashify %w(a b c)
=> {"a"=>1, "b"=>2, "c"=>3}
Try this, this will add a method to_hash_one_indexed to the Array class.
class Array
def to_hash_one_indexed
map.with_index(1).to_h
end
end
Then to call it:
%w(a b c).to_hash_one_indexed
#=> {"a"=>1, "b"=>2, "c"=>3}

Differences between these 2 Ruby enumerators: [1,2,3].map vs. [1,2,3].group_by

In Ruby, is there a functional difference between these two Enumerators?
irb> enum_map = [1,2,3].map
=> #<Enumerator: [1, 2, 3]:map> # ends with "map>"
irb> enum_group_by = [1,2,3].group_by
=> #<Enumerator: [1, 2, 3]:group_by> # ends with "group_by>"
irb> enum_map.methods == enum_group_by.methods
=> true # they have the same methods
What can #<Enumerator: [1, 2, 3]:map> do that <Enumerator: [1, 2, 3]:group_by> can't do, and vice versa?
Thanks!
From the documentation of group_by:
Groups the collection by result of the block. Returns a hash where the
keys are the evaluated result from the block and the values are arrays
of elements in the collection that correspond to the key.
If no block is given an enumerator is returned.
(1..6).group_by { |i| i%3 } #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
From the documentation of map:
Returns a new array with the results of running block once for every
element in enum.
If no block is given, an enumerator is returned instead.
(1..4).map { |i| i*i } #=> [1, 4, 9, 16]
(1..4).collect { "cat" } #=> ["cat", "cat", "cat", "cat"]
As you can see, each does something different, which serves a different purpose. Concluding that two APIs are the same because they expose the same interface seems to miss the entire purpose of Object Oriented Programming - different services are supposed to expose the same interface to enable polymorphism.
There's a difference in what they do, but fundamentally they are both of the same class: Enumerator.
When they're used the values emitted by the enumerator will be different, yet the interface to them is identical.
Two objects of the same class generally have the same methods. It is possible to augment an instance with additional methods, but this is not normally done.

Cleanly converting a {"key" => ["val1", "val2"]} hash into a {"key" => "val1", "key" => "val2"} hash in Ruby/Rails

I'm fairly new to Ruby/Rails, and I'm trying to figure out how to split a {"key" => ["val1", "val2"]} hash into a {"key" => "val1", "key" => "val2"} hash. I feel like I should flatten the hash and somehow build a new one up, but I'm unsure how to approach the problem. Thanks!
EDIT: Haha, shows how blinded I was by the trees to not see the forest. Can't believe I made such a silly mistake. Thanks to everyone who shook me awake.
You cannot have duplicate keys in a Hash.
Also, why in the world would you want to do this? IMHO the way you have it now is perfectly fine.
A Hash by definition cannot have the same key present more than once. Would you instead like an Array of arrays?
[['key','val1'],['key','val2']]
If so, and if every hash key is an array of values, then you can do this:
devalues = { a:[1,2,3], b:[4], c:[5,6] }
exploded = devalues.map{ |k,vs| ([k]*vs.length).zip(vs) }.flatten(1)
p exploded
#=> [[:a, 1], [:a, 2], [:a, 3], [:b, 4], [:c, 5], [:c, 6]]
Note that flatten(1) is Ruby 1.8.7+ only
Edit: Per Nakilon's comment below, this can be a hair simpler in Ruby 1.9.2+:
exploded = devalues.flat_map{ |k,vs| ([k]*vs.length).zip(vs) }
Edit: Or per #tokland's comment below, even shorter/better using Array#product:
exploded = devalues.flat_map{ |k,vs| [k].product(vs) }

Deleting multiple key and value pairs from hash in Rails

number = {:a => 1, :b => 2, :c => 3, :d => 4}
upon evaluation of certain condition i want to delete key-value pair of a,b,c
number.delete "A"
number.delete "B"
number.delete "C"
Or, less performant but more terse:
number.reject! {|k, v| %w"A B C".include? k }
or, more performant than second Chris' solution but shorter than first:
%w"A B C".each{|v| number.delete(v)}
ActiveSupport that is a part of Rails comes with several built-in methods can help you to achieve your goal.
If you just want to delete some key-value pairs, you can use Hash#except!
number.except!(:a, :b, :c)
If you want to keep the original hash, then use Hash#except
new_hash = number.except!(:a, :b, :c)
new_hash # => {:d=>4}
number # => {:a=>1, :b=>2, :c=>3, :d=>4}
You also can go with Rails-free way:
new_hash = number.dup.tap do |hash|
%i[a b c].each {|key| hash.delete(key)}
end
new_hash # => {:d=>4}
number # => {:a=>1, :b=>2, :c=>3, :d=>4}
P.S.: the last code example is very slow, I'm just providing it as an alternative.

How do I get the unique elements from an array of hashes in Ruby?

I have an array of hashes, and I want the unique values out of it. Calling Array.uniq doesn't give me what I expect.
a = [{:a => 1},{:a => 2}, {:a => 1}]
a.uniq # => [{:a => 1}, {:a => 2}, {:a => 1}]
Where I expected:
[{:a => 1}, {:a => 2}]
In searching around on the net, I didn't come up with a solution that I was happy with. Folks recommended redefining Hash.eql? and Hash.hash, since that is what Array.uniq is querying.
Edit:
Where I ran into this in the real world, the hashes were slightly more complex. They were the result of parsed JSON that had multiple fields, some of which the values were hashes as well. I had an array of those results that I wanted to filter out the unique values.
I don't like the redefine Hash.eql? and Hash.hash solution, because I would either have to redefine Hash globally, or redefine it for each entry in my array. Changing the definition of Hash for each entry would be cumbersome, especially since there may be nested hashes inside of each entry.
Changing Hash globally has some potential, especially if it were done temporarily. I'd want to build another class or helper function that wrapped saving off the old definitions, and restoring them, but I think this adds more complexity than is really needed.
Using inject seems like a good alternative to redefining Hash.
I can get what I want by calling inject
a = [{:a => 1},{:a => 2}, {:a => 1}]
a.inject([]) { |result,h| result << h unless result.include?(h); result }
This will return:
[{:a=>1}, {:a=>2}]
Ruby 1.8.7+ will return just what you have expected:
[{:a=>1}, {:a=>2}, {:a=>1}].uniq
#=> [{:a=>1}, {:a=>2}]
I've had a similar situation, but hashes had keys. I used sorting method.
What I mean:
you have an array:
[{:x=>1},{:x=>2},{:x=>3},{:x=>2},{:x=>1}]
you sort it (#sort_by {|t| t[:x]}) and get this:
[{:x=>1}, {:x=>1}, {:x=>2}, {:x=>2}, {:x=>3}]
now a bit modified version of answer by Aaaron Hinni:
your_array.inject([]) do |result,item|
result << item if !result.last||result.last[:x]!=item[:x]
result
end
I've also tried:
test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]}
but it's very slow. here is my benchmark:
test=[]
1000.times {test<<{:x=>rand}}
Benchmark.bmbm do |bm|
bm.report("sorting: ") do
test.sort_by {|t| t[:x]}.inject([]) {|r,h| r<<h if !r.last||r.last[:x]!=h[:x]; r}
end
bm.report("inject: ") {test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]} }
end
results:
Rehearsal ---------------------------------------------
sorting: 0.010000 0.000000 0.010000 ( 0.005633)
inject: 0.470000 0.140000 0.610000 ( 0.621973)
------------------------------------ total: 0.620000sec
user system total real
sorting: 0.010000 0.000000 0.010000 ( 0.003839)
inject: 0.480000 0.130000 0.610000 ( 0.612438)
Assuming your hashes are always single key-value pairs, this will work:
a.map {|h| h.to_a[0]}.uniq.map {|k,v| {k => v}}
Hash.to_a creates an array of key-value arrays, so the first map gets you:
[[:a, 1], [:a, 2], [:a, 1]]
uniq on Arrays does what you want, giving you:
[[:a, 1], [:a, 2]]
and then the second map puts them back together as hashes again.
You can use (tested in ruby 1.9.3),
[{a: 1},{a: 2},{a:1}].uniq => [{a:1},{a: 2}]
[{a: 1,b: 2},{a: 2, b: 2},{a: 1, b: 3}].uniq_by {|v| v[:a]} => [{a: 1,b: 2},{a: 2, b: 2}]
The answer you give is similar to the one discussed here. It overrides the hash and eql? methods on the hashes that are to appear in the array which then makes uniq behave correctly.
found on google
http://mikeburnscoder.wordpress.com/2008/01/18/uniquify-an-array-of-hashes-in-ruby/
The pipe method on arrays (available since 1.8.6) performs set union (returning an array), so the following is another possible way to get unique elements of any array a:
[] | a

Resources