Using Hash.new as an initial value when reducing an array - ruby

I have an array like so
[1,1,2,3,3,3,4,5,5]
and I want to count the number of occurrences of each number, which I'm trying to do like so
[1,1,2,3,3,3,4,5,5].reduce(Hash.new(0)) { |hash,number| hash[number] += 1 }
The problem is I get the following error when I try to run it
NoMethodError: undefined method `[]=' for 1:Fixnum
from (irb):6:in `block in irb_binding'
from (irb):6:in `each'
from (irb):6:in `reduce'
from (irb):6
Am I able to set the initial value like this, or am I getting this wrong?

You can use each_with_object for cleaner syntax
[1,1,2,3,3,3,4,5,5].each_with_object(Hash.new(0)) { |number, hash| hash[number] += 1 }
Note that order of arguments is reverse i.e. |number, hash|

You can use reduce, but if you want so, you have to return the hash again:
[1,1,2,3,3,3,4,5,5].reduce(Hash.new(0)) do |hash,number|
hash[number] += 1
hash
end
Without that the value of hash[number] would be returned. The value of a hash assignment is the value itself. The block builds on the value you have returned previously.
Which means, that after the first it would try something like this: 1[1] += 1, which of course does not work, because Fixnums do not implement the method []=.

I like to use reduce with hash.update. It returns the array and I do not need the ; hash part:
[1,1,2,3,3,3,4,5,5].reduce(Hash.new(0)) { |h, n| h.update(n => h[n].next) }

Your question has been answered, but here's another way to skin the cat:
[Edited to adopt #David's suggestion.]
a = [1,1,2,3,3,3,4,5,5]
Hash[a.group_by(&:to_i).map {|g| [g.first, g.last.size]}]
This also works:
a.group_by{|e| e}.inject({}) {|h, (k,v)| h[k] = v.size; h}
and can be improved by adopting #spickermann's use of update (or it's synonym, merge!), to get rid of that irritating ; h at the end:
a.group_by{|e| e}.inject({}) {|h, (k,v)| h.merge!(k => v.size)}
Formerly, I had:
Hash[*a.group_by(&:to_i).to_a.map {|g| [g.first, g.last.size]}.flatten]
I don't like to_i here. I wanted to use to_proc instead of ...group_by {|x| x|} (as used in one of the solutions above) and was looking for a method m that returns the receiver or its value. to_i was the best I could do (e.g., 2.to_i => 2), but only for Integers. Can anyone suggest a method that returns the receiver for a wide range of objects whose use with to_proc makes it's purpose obvious?

That's simple:
[1,1,2,3,3,3,4,5,5].group_by {|e| e}.collect {|k,v| [k,v.count]}
#=> [[1, 2], [2, 1], [3, 3], [4, 1], [5, 2]]
if result required in a Hash object
Hash[[1,1,2,3,3,3,4,5,5].group_by {|e| e}.collect {|k,v| [k,v.count]}]
#=> {1=>2, 2=>1, 3=>3, 4=>1, 5=>2}

Related

How use dig method several for children object

I have an hash
{:result=>
{:"1"=>
[{:"1"=>1,
:"2"=>"4698192612070913717",
:"5"=>
{:"1"=>{:"1"=>"1.0.0"},
:"2"=>
{:"1"=>1,
:"2"=>"1525341956127",
:"3"=>1000000000,
:"4"=>0,
:"5"=>{:"1"=>1000000000}},
:"3"=>["17"],
:"6"=>"4704522736971289334",
:"8"=>["4618851880555471022"],
:"9"=>[1]},
:"6"=>{:"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"7"=>{:"1"=>1},
:"8"=>"production"},
{:"1"=>4,
:"2"=>"4700283765268993541",
:"6"=>{:"2"=>{:"1"=>200}, :"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"8"=>"beta"},
{:"1"=>5,
:"2"=>"4699074054925986704",
:"6"=>{:"2"=>{:"1"=>100}, :"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"8"=>"alpha"},
{:"1"=>10,
:"2"=>"4697702456121346981",
:"6"=>{:"2"=>{:"1"=>50}, :"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"8"=>"internal"}],
:"3"=>{:"1"=>true, :"2"=>{:"1"=>{:"1"=>false}, :"2"=>{:"1"=>false}}},
:"4"=>false},
:xsrf=>"AMtNNDFJl06mR54j2zxFjYIYfGQR22sUKA:1528830206790"}
I am looking for simple way to return a value or nil
I have tried this
result[:'result'][:'1'][1].dig(:'5').dig(:'1').dig(:'1')
but it's not working
What can I do to avoid this
if result[:'result'][:'1'][1].dig(:'5')
puts result[:'result'][:'1'][1][:'5'][:'1'][:'1']
end
The idea behind dig is that you can go several levels deep into a hash at the same time and return nil if the key doesn't exist at any level during the 'digging'. So result[:'result'][:'1'][1].dig(:'5', :'1', :'1') will do what you are looking for and clean up your code as well. In fact, you could make it a little safer if you wanted by doing result.dig(:result, :'1', 1, :'5', :'1', :'1')
dig is not a single method, but a family of four methods, all of which made their debut in Ruby v2.3: Array#dig, Hash#dig, Struct#dig and OpenStruct#dig.
For example,
h = { a: [1, { c: 2, d: 3 }], b: 2 }
h.dig(:a, 1, :d)
#=> 3
employs Hash#dig because dig's receiver is a hash. Moreover, one might expect that when, in an intermediate calculation, dig has unearthed [1, { c: 2, d: 3 }] it will pass the shovel to Array#dig for further excavation.
Suppose
h = { a: [1, 2] }
Then
h.dig(:a, 1) #=> 2
h.dig(:a).dig(1) #=> 2
Does that mean the two are equivalent? Try this:
h.dig('cat', 1) #=> nil
h.dig('cat').dig(1) #=> NoMethodError: undefined method `dig' for nil:NilClass
The exception is due to the fact that h.dig('cat') #=> nil and NilClass has no instance method dig, so nil.dig(1) raises the exception. No, the two expressions are not equivalent.
If the value of the variable result is the OPs hash, we have (as pointed out by #Isaiah) the following.
result.dig(:result, :'1', 0, :"5", :"1", :"1")
#=> "1.0.0"
result.dig(:result, :'1', 0, :cat, :"1", :"1")
#=> nil
Note that dig will still raise an exception if the wrong data type is used:
[1, 2].dig(:a)
#=> TypeError: no implicit conversion of Symbol into Integer
To support versions of Ruby prior to 2.3 (where dig is not available) we can write the following, using Enumerable#reduce (aka inject).
arr = [:result, :'1', 0, :"5", :"1", :"1"]
arr.reduce(result) { |memo, obj| memo && memo[obj] }
#=> "1.0.0"
arr = [:result, :'1', 0, :cat, :"1", :"1"]
arr.reduce(result) { |memo, obj| memo && memo[obj] }
#=> nil

Differences between [1,2,3].to_enum and [1,2,3].enum_for in Ruby

In Ruby I'm trying to understand between the to_enum and enum_for methods. Before I my question, I've provided some sample code and two examples to help w/ context.
Sample code:
# replicates group_by method on Array class
class Array
def group_by2(&input_block)
return self.enum_for(:group_by2) unless block_given?
hash = Hash.new {|h, k| h[k] = [] }
self.each { |e| hash[ input_block.call(e) ] << e }
hash
end
end
Example # 1:
irb (main)> puts [1,2,3].group_by2.inspect
=> #<Enumerator: [1, 2, 3]:group_by2>
In example #1: Calling group_by on the array [1,2,3], without passing in a block, returns an enumerator generated with the command self.enum_for(:group_by_2).
Example #2
irb (main)> puts [1,2,3].to_enum.inspect
=> #<Enumerator: [1, 2, 3]:each>
In example #2, the enumerator is generated by calling the to_enum method on the array [1,2,3]
Question:
Do the enumerators generates in examples 1 and 2, behave differently in any way? I can see from the inspected outputs that they show slightly different labels, but I can find any difference in the enumerators' behavior.
# Output for example #1
#<Enumerator: [1, 2, 3]:each> # label reads ":each"
# Output for example #2
#<Enumerator: [1, 2, 3]:group_by2> # label reads ":group_by2"
p [1, 2, 3].to_enum
p [1, 2, 3].enum_for
--output:--
#<Enumerator: [1, 2, 3]:each>
#<Enumerator: [1, 2, 3]:each>
From the docs:
to_enum
Creates a new Enumerator which will enumerate by calling method on
obj, passing args if any.
...
enum_for
Creates a new Enumerator which will enumerate by calling method on
obj, passing args if any.
ruby is a language that often has method names that are synonyms.
Followup question:
Does the symbol in the command [1,2,3].to_enum(:foo) serve a purpose,
other than replacing :each with :foo in the output?
Yes. By default, ruby hooks up the enumerator to the receiver's each() method. Some classes do not have an each() method, for instance String:
str = "hello\world"
e = str.to_enum
puts e.next
--output:--
1.rb:3:in `next': undefined method `each' for "helloworld":String (NoMethodError)
from 1.rb:3:in `<main>
to_enum() allows you to specify the method you would like the enumerator to use:
str = "hello\nworld"
e = str.to_enum(:each_line)
puts e.next
--output:--
hello
Now, suppose you have the array [1, 2, 3], and you want to to create an enumerator for your array. An array has an each() method, but instead of creating an enumerator with each(), which will return each of the elements in the array, then end; you want to create an enumerator that starts over from the beginning of the array once it reaches the end?
e = [1, 2, 3].to_enum(:cycle)
10.times do
puts e.next()
end
--output:--
1
2
3
1
2
3
1
2
3
1

Using inject with an array of hashes

I have an array of hashes, each with a key lol which has an integer value. I'd like to sum the values, inject always worked but now I get an exception:
array = [{lol: 1}, {lol: 2}]
array.inject {|memo, (key, value)| memo + value} =>
NoMethodError: undefined method `+' for {:lol=>1}:Hash
from (irb):26:in `block in irb_binding'
from (irb):26:in `each'
from (irb):26:in `inject'
from (irb):26
Por que?
You can just get all the hash values with flat_map(&:values), then use inject(:+) to sum them.
[{lol: 1}, {lol: 2}].flat_map(&:values).inject(:+)
The reason your approach doesn't work is that inject is going to yield each hash to the block, rather than each key/value pair of each hash in the array. If you wanted to keep your solution, you'd want something like:
array.map {|hash| hash.inject(0) {|memo, (key, value)| memo + value } }.inject(:+)
From .inject documentation
If you do not explicitly specify an initial value for memo, then the first element of collection is used as the initial value of memo.
array.inject {|memo, (key, value)| memo + value}
The value for memo is a hash instead of a number.
Correct it as
array.inject(0) {|memo, hash| memo + hash[:lol]} # => 3
If you don't specify an argument to inject, the value for the memo object for the first iteration is the first element of the enumerable, an hash in this case. So you just have to pass 0 as the argument to inject:
array = [{lol: 1}, {lol: 2}]
array.inject(0) { |sum, h| sum + h[:lol] }
# => 3

How do I pass a lambda to Hash.each?

How can I pass a lambda to hash.each, so I can re-use some code?
> h = { a: 'b' }
> h.each do |key, value| end
=> {:a=>"b"}
> test = lambda do |key, value| puts "#{key} = #{value}" end
> test.call('a','b')
a = b
> h.each &test
ArgumentError: wrong number of arguments (1 for 2)
from (irb):1:in `block in irb_binding'
from (irb):5:in `each'
from (irb):5
from /Users/jstillwell/.rvm/rubies/ruby-1.9.3-p362/bin/irb:16:in `<main>'
> h.each test
ArgumentError: wrong number of arguments(1 for 0)
from (irb):8:in `each'
from (irb):8
from /Users/jstillwell/.rvm/rubies/ruby-1.9.3-p362/bin/irb:16:in `<main>'
each yields the current element to the block, ergo the lambda needs to take one argument, not two. In case of a Hash the element being yielded is a two-element Array with the first element being the key and the second element being the value.
test = lambda do |el| puts "#{el.first} = #{el.last}" end
h.each &test
# a = b
Alternatively, you can use Ruby's support for destructuring bind in parameter lists:
test = lambda do |(k, v)| puts "#{k} = #{v}" end
h.each &test
# a = b
Or, instead of using a lambda which has the same argument binding semantics as a method, you could use a Proc, which has the same argument binding semantics as a block, i.e. it will unpack a single Array argument into multiple parameter bindings:
test = proc do |k, v| puts "#{k} = #{v}" end
h.each &test
# a = b
I still feel this is inconsistent syntax, I found the answer (but no apology) in another question
Inconsistency of arity between Hash.each and lambdas
I switched it to
lambda do |(key, value)|
then I can pass in
hash.each &test
or I can call it directly with
test.call([key, value])
If someone has a better answer, or at least a succinct excuse why this is necessary. I'll gladly give them the points.
I have come across a similar case where I wanted not to print but to return a key-value pair after doing some operation with the value... the above solution applies when you just want to print but it can become tricky when you need to return a Hash of those key-value pairs. so I thought this could be helpful
I used it to solve the caesar-cipher puzzle
hash1 = {"a" => 1, "b" => 2, "c" => 3}
test = lambda {|k,v| true ? {k => v+2} : {k => v+1} }
after mapping this to an hash it will return an array of Hashes (here is where things get interesting)
hash2 = hash1.map(&test) # [{"a"=>3}, {"b"=>4}, {"c"=>5}]
we can convert it to hash rubystically!
hash2.inject(:merge!) # {"a" => 3, "b" => 4, "c" => 5}
Bingo!!! right?

Sort items in a nested hash by their values

I'm being sent a nested hash that needs to be sorted by its values. For example:
#foo = {"a"=>{"z"=>5, "y"=>3, "x"=>88}, "b"=>{"a"=>2, "d"=>-5}}
When running the following:
#foo["a"].sort{|a,b| a[1]<=>b[1]}
I get:
[["y", 3], ["z", 5], ["x", 88]]
This is great, it's exactly what I want. The problem is I'm not always going to know what all the keys are that are being sent to me so I need some sort of loop. I tried to do the following:
#foo.each do |e|
e.sort{|a,b| a[1]<=>b[1]}
end
This to me makes sense since if I manually call #foo.first[0] I get
"a"
and #foo.first[1] returns
{"z"=>5, "y"=>3, "x"=>8}
but for some reason this isn't sorting properly (e.g. at all). I assume this is because the each is calling sort on the entire hash object rather than on "a"'s values. How do I access the values of the nested hash without knowing what it's key is?
You might want to loop over the hash like this:
#foo.each do |key, value|
#foo[key] = value.sort{ |a,b| a[1]<=>b[1] }
end
#foo = {"a"=>{"z"=>5, "y"=>3, "x"=>88}, "b"=>{"a"=>2, "d"=>-5}}
#bar = Hash[ #foo.map{ |key,values| [ key, values.sort_by(&:last) ] } ]
Or, via a less-tricky path:
#bar = {}
#foo.each do |key,values|
#bar[key] = values.sort_by{ |key,value| value }
end
In both cases #bar turns out to be:
p #bar
#=> {
#=> "a"=>[["y", 3], ["z", 5], ["x", 88]],
#=> "b"=>[["d", -5], ["a", 2]]
#=> }
My coworker came up with a slightly more flexible solution that will recursively sort an array of any depth:
def deep_sort_by(&block)
Hash[self.map do |key, value|
[if key.respond_to? :deep_sort_by
key.deep_sort_by(&block)
else
key
end,
if value.respond_to? :deep_sort_by
value.deep_sort_by(&block)
else
value
end]
end.sort_by(&block)]
end
You can inject it into all hashes and then just call it like this:
myMap.deep_sort_by { |obj| obj }
The code would be similar for an array. We published it as a gem for others to use, see blog post for additional details.
Disclaimer: I work for this company.
in your example e is an temporary array containing a [key,value] pair. In this case, the character key and the nested hash. So e.sort{|a,b|...} is going to try to compare the character to the hash, and fails with a runtime error. I think you probably meant to type e[1].sort{...}. But even that is not going to work correctly, because you don't store the sorted hash anywhere: #foo.each returns the original #foo and leaves it unchanged.
The better solution is the one suggested by #Pan Thomakos:
#foo.each do |key, value|
#foo[key] = value.sort{ |a,b| a[1]<=>b[1] }
end

Resources