Checking arrays and implementing bool methods - ruby

You have an array. If any two numbers add to zero in the array, return true. It doesn't matter how many pairs there are—as long as there is one pair that adds to zero, return true. If there is a zero, it can only return true if there is more than one.
I wrote two functions, one to check for each, and a final one to combine both, and return false if either aren't met.
def checkZero(array)
zerocount = 0
for j in 0..array.count
if array[j] == 0
zerocount += 1
end
end
if zerocount > 1 #this part seems to not be working, not sure why
return true
else
return false
end
end
def checkNegative(array)
for j in 0..array.count
neg = -array[j] #set a negative value of the current value
if array.include?(neg) #check to see whether the negative exists in the array
return true
else
return false
end
end
end
def checkArray(array)
if checkZero(array) == true or checkNegative(array) == true
return true
else
return false
end
end
Then run something like
array = [1,2,3,4,0,1,-1]
checkArray(array)
So far, Ruby isn't returning anything. I just get a blank. I have a feeling my return isn't right.

The problem may be that you didn't output the result.
array = [1,2,3,4,0,1,-1]
puts checkArray(array)
The checkArray method can be written like the following, if performance (O(n^2)) is not a great concern:
def check_array(array)
array.combination(2).any?{|p| p.reduce(:+) == 0}
end
The more efficient (O(n log n)) solution is:
def check_array(array)
array.sort! # `array = array.sort` if you need the original array unchanged
i, j = 0, array.size - 1
while i < j
sum = array[i] + array[j]
if sum > 0
j -= 1
elsif sum < 0
i += 1
else
return true
end
end
return false
end

Here's are a few relatively efficient ways to check if any two values sum to zero:
Solution #1
def checkit(a)
return true if a.count(&:zero?) > 1
b = a.uniq.map(&:abs)
b.uniq.size < b.size
end
Solution #2
def checkit(a)
return true if a.sort_by(&:abs).each_cons(2).find { |x,y| x == -y }
false
end
Solution #3
def checkit(a)
return true if a.count(&:zero?) > 1
pos, non_pos = a.group_by { |n| n > 0 }.values
(pos & non_pos.map { |n| -n }).any?
end
Solution #4
require 'set'
def checkit(a)
a.each_with_object(Set.new) do |n,s|
return true if s.include?(-n)
s << n
end
false
end
Examples
checkit([1, 3, 4, 2, 2,-3,-5,-7, 0, 0]) #=> true
checkit([1, 3, 4, 2, 2,-3,-5,-7, 0]) #=> true
checkit([1, 3, 4, 2,-3, 2,-3,-5,-7, 0]) #=> true
checkit([1, 3, 4, 2, 2,-5,-7, 0]) #=> false
Explanations
The following all refer to the array:
a = [1,3,4,2,2,-3,-5,-7,0]
#1
Zeroes present a bit of a problem, so lets first see if there are more than one, in which case we are finished. Since a.count(&:zero?) #=> 1, a.count(&:zero?) > 1 #=> false, so
return true if a.count(&:zero?) > 1
does not cause us to return. Next, we remove any duplicates:
a.uniq #=> [1, 3, 4, 2, -3, -5, -7, 0]
Then convert all the numbers to their absolute values:
b = a.uniq,map(&:abs) #=> [1, 3, 4, 2, 3, 5, 7, 0]
Lastly see if c contains any dups, meaning the original array contained at least two non-zero numbers with opposite signs:
c.uniq.size < c.size #=> true
#2
b = a.sort_by(&:abs)
#=> [0, 1, 2, 2, 3, -3, 4, -5, -7]
c = b.each_cons(2)
#=> #<Enumerator: [0, 1, 2, 2, 3, -3, 4, -5, -7]:each_cons(2)>
To see the contents of the enumerator:
c.to_a
#=> [[0, 1], [1, 2], [2, 2], [2, 3], [3, -3], [-3, 4], [4, -5], [-5, -7]]
c.find { |x,y| x == -y }
#=> [3, -3]
so true is returned.
#3
return true if a.count(&:zero?) > 1
#=> return true if 1 > 1
h = a.group_by { |n| n > 0 }
#=> {true=>[1, 3, 4, 2, 2], false=>[-3, -5, -7, 0]}
b = h.values
#=> [[1, 3, 4, 2, 2], [-3, -5, -7, 0]]
pos, non_pos = b
pos
#=> [1, 3, 4, 2, 2]
non_pos
#=> [-3, -5, -7, 0]
c = non_pos.map { |n| -n }
#=> [3, 5, 7, 0]
d = pos & c
#=> [3]
d.any?
#=> true
#4
require 'set'
enum = a.each_with_object(Set.new)
#=> #<Enumerator: [1, 3, 4, 2, 2, -3, -5, -7, 0]:each_with_object(#<Set: {}>)>
enum.to_a
#=> [[1, #<Set: {}>],
# [3, #<Set: {}>],
# ...
# [0, #<Set: {}>]]
Values are passed into the block, assigned to the block variables and the block is executed, as follows:
n, s = enum.next
#=> [1, #<Set: {}>]
s.include?(-n)
#=> #<Set: {}>.include?(-1)
#=> false
s << n
#=> #<Set: {1}>
n, s = enum.next
#=> [3, #<Set: {1}>]
s.include?(-3)
#=> false
s << n
#=> #<Set: {1, 3}>
...
n, s = enum.next
#=> [2, #<Set: {1, 3, 4, 2}>]
s.include?(-n)
#=> false
s << n
#=> #<Set: {1, 3, 4, 2}> # no change
n, s = enum.next
#=> [-3, #<Set: {1, 3, 4, 2}>]
s.include?(-n)
#=> true
causing true to be returned.

I can’t reproduce any problem with your code, but you can express the solution very succinctly using combination to get all possible pairs, then summing each pair with reduce, and finally checking if any are zero?:
[1,2,3,4,0,1,-1].combination(2).map { |pair| pair.reduce(:+) }.any?(&:zero?)

This is a bit of a code review. Let's start with the first method:
def checkZero(array)
Ruby naming convention is snake_case rather than camelCase. This should be def check_zero(array)
Now the loop:
zerocount = 0
for j in 0..array.count
if array[j] == 0
zerocount += 1
end
end
As #AndrewMarshall said, for is not idiomatic. each is preferable. However, in ruby initializing a variable before a loop is almost never needed thanks to all the methods available to you on Array and Enumerable (which is included in Array). I highly recommend committing these methods to memory. The above can be written
array.any? {|number| number.zero?}
or equivalently
array.any?(&:zero?)
Now, this part:
if zerocount > 1 #this part seems to not be working, not sure why
return true
else
return false
end
end
Whenever you have the pattern
if (expr that returns true or false)
return true
else
return false
end
it can be simplified to simply return (expr that returns true or false). And you can even omit the return if it is the last statement of a method.
Putting it all together:
def check_zero(array)
array.any?(&:zero?)
end
def check_zero_sum(array)
array.combination(2).any?{|a,b| a + b == 0}
end
def check_array(array)
check_zero(array) || check_zero_sum(array)
end
(Note I borrowed AndrewMarshall's code for check_zero_sum which I think is easy to follow, but #CarySwoveland's answer will be faster)
Edit
I missed the fact that check_zero isn't even necessary because you want at least a pair, in which case check_zero_sum is all you need.
def check_array(array)
array.combination(2).any?{|a,b| a + b == 0}
end

Related

Birthday Chocolate HACKERRANK RUBY

This is the original link for the problem in hackerrank: https://www.hackerrank.com/challenges/the-birthday-bar/problem
I have been fighting with this problem in Ruby and I don't know why my counter always returns 1. This is the solution. I hope you can help me to understand what I'm making wrong.
s = [1, 2, 1, 3, 2]
d = 3
m = 2
def birthday(s, d, m)
array = []
cont = 0
sum = 0
m.times {array.push(s.shift)}
(m-1).times do
array.each {|i| sum = sum + i}
if sum == d
cont += 1
end
array.shift
array.push(s.shift)
end
return cont
end
birthday(s, d, m)
Though the following does not answer your question directly, it is a Ruby-like way of solving the problem, especially by making use of the methods Enumerable#each_cons and Enumerable#count.
def birthday(s, d, m)
s.each_cons(m).count { |a| a.sum == d }
end
s = [1, 2, 1, 3, 2]
d = 3
m = 2
birthday(s, d, m)
#=> 2 ([1, 2] and [2, ])
s = [2, 2, 1, 3, 2]
d = 4
m = 2
birthday(s, d, m)
#=> 2 ([2, 2] and [1, 3])
s = [2, 4, 3, 2, 1, 2, 6, 1]
d = 9
m = 3
birthday(s, d, m)
#=> 4 ([2, 4, 3], [4, 3, 2], [1, 2, 6] and [2, 6, 1])
Notice from the doc that when each_cons is used without a block it returns an enumerator:
s = [1, 2, 1, 3, 2]
d = 3
m = 2
enum = s.each_cons(m)
#=> #<Enumerator: [1, 2, 1, 3, 2]:each_cons(2)>
enum will generate elements and pass them to count until there are no more to generate, at which time it raises a StopIteration exception:
enum.next #=> [1, 2]
enum.next #=> [2, 1]
enum.next #=> [1, 3]
enum.next #=> [3, 2]
enum.next #=> StopIteration (iteration reached an end) <exception>
We can write1:
enum.count { |a| a.sum == d }
#=> 2
After enum generates the first value ([1, 2]) the block variable a is assigned its value:
a = enum.next
#=> [1, 2]
and the block calculation is performed. As
a.sum == d
#=> [1, 2].sum == 3 => true
the count is incremented (from zero) by one. enum then passes each of its remaining values to count and the process is repeated. When, for example, [1, 3].sum == 3 => false is executed, the count is not incremented.
1. Note that since I just stepped through all the elements of enum, enum.next would generate another StopIteration exception. To execute enum.count { |a| a.sum == d } I therefore must first redefine the enumerator (enum = s.each_cons(m)) or Enumerator#rewind it: enum.rewind.

Ruby: How to find the most frequent substring of length n? [duplicate]

I have this program with a class DNA. The program counts the most frequent k-mer in a string. So, it is looking for the most common substring in a string with a length of k.
An example would be creating a dna1 object with a string of AACCAATCCG. The count k-mer method will look for a subtring with a length of k and output the most common answer. So, if we set k = 1 then 'A' and 'C' will be the most occurrence in the string because it appears four times. See example below:
dna1 = DNA.new('AACCAATCCG')
=> AACCAATCCG
>> dna1.count_kmer(1)
=> [#<Set: {"A", "C"}>, 4]
>> dna1.count_kmer(2)
=> [#<Set: {"AA", "CC"}>, 2]
Here is my DNA class :
class DNA
def initialize (nucleotide)
#nucleotide = nucleotide
end
def length
#nucleotide.length
end
protected
attr_reader :nucleotide
end
Here is my count kmer method that I am trying to implement:
# I have k as my only parameter because I want to pass the nucleotide string in the method
def count_kmer(k)
# I created an array as it seems like a good way to split up the nucleotide string.
counts = []
#this tries to count how many kmers of length k there are
num_kmers = self.nucleotide.length- k + 1
#this should try and look over the kmer start positions
for i in num_kmers
#Slice the string, so that way we can get the kmer
kmer = self.nucleotide.split('')
end
#add kmer if its not present
if !kmer = counts
counts[kmer] = 0
#increment the count for kmer
counts[kmer] +=1
end
#return the final count
return counts
end
#end dna class
end
I'm not sure where my method went wrong.
Something like this?
require 'set'
def count_kmer(k)
max_kmers = kmers(k)
.each_with_object(Hash.new(0)) { |value, count| count[value] += 1 }
.group_by { |_,v| v }
.max
[Set.new(max_kmers[1].map { |e| e[0] }), max_kmers[0]]
end
def kmers(k)
nucleotide.chars.each_cons(k).map(&:join)
end
EDIT: Here's the full text of the class:
require 'set'
class DNA
def initialize (nucleotide)
#nucleotide = nucleotide
end
def length
#nucleotide.length
end
def count_kmer(k)
max_kmers = kmers(k)
.each_with_object(Hash.new(0)) { |value, count| count[value] += 1 }
.group_by { |_,v| v }
.max
[Set.new(max_kmers[1].map { |e| e[0] }), max_kmers[0]]
end
def kmers(k)
nucleotide.chars.each_cons(k).map(&:join)
end
protected
attr_reader :nucleotide
end
This produces the following output, using Ruby 2.2.1, using the class and method you specified:
>> dna1 = DNA.new('AACCAATCCG')
=> #<DNA:0x007fe15205bc30 #nucleotide="AACCAATCCG">
>> dna1.count_kmer(1)
=> [#<Set: {"A", "C"}>, 4]
>> dna1.count_kmer(2)
=> [#<Set: {"AA", "CC"}>, 2]
As a bonus, you can also do:
>> dna1.kmers(2)
=> ["AA", "AC", "CC", "CA", "AA", "AT", "TC", "CC", "CG"]
Code
def most_frequent_substrings(str, k)
(0..str.size-k).each_with_object({}) do |i,h|
b = []
str[i..-1].scan(Regexp.new str[i,k]) { b << Regexp.last_match.begin(0) + i }
(h[b.size] ||= []) << b
end.max_by(&:first).last.each_with_object({}) { |a,h| h[str[a.first,k]] = a }
end
Example
str = "ABBABABBABCATSABBABB"
most_frequent_substrings(str, 4)
#=> {"ABBA"=>[0, 5, 14], "BBAB"=>[1, 6, 15]}
This shows that the most frequently-occurring 4-character substring of strappears 3 times. There are two such substrings: "ABBA" and "BBAB". "ABBA" begins at offsets (into str) 0, 5 and 14, "BBAB" substrings begin at offsets 1, 6 and 15.
Explanation
For the example above the steps are as follows.
k = 4
n = str.size - k
#=> 20 - 4 => 16
e = (0..n).each_with_object([])
#<Enumerator: 0..16:each_with_object([])>
We can see the values that will be generated by this enumerator by converting it to an array.
e.to_a
#=> [[0, []], [1, []], [2, []], [3, []], [4, []], [5, []], [6, []], [7, []], [8, []],
# [9, []], [10, []], [11, []], [12, []], [13, []], [14, []], [15, []], [16, []]]
Note the empty array contained in each element will be modified as the array is built. Continuing, the first element of e is passed to the block and the block variables are assigned using parallel assignment:
i,a = e.next
#=> [0, []]
i #=> 0
a #=> []
We are now considering the substring of size 4 that begins at str offset i #=> 0, which is seen to be "ABBA". Now the block calculation is performed.
b = []
r = Regexp.new str[i,k]
#=> Regexp.new str[0,4]
#=> Regexp.new "ABBA"
#=> /ABAB/
str[i..-1].scan(r) { b << Regexp.last_match.begin(0) + i }
#=> "ABBABABBABCATSABBABB".scan(r) { b << Regexp.last_match.begin(0) + i }
b #=> [0, 5, 14]
We next have
(h[b.size] ||= []) << b
which becomes
(h[b.size] = h[b.size] || []) << b
#=> (h[3] = h[3] || []) << [0, 5, 14]
Since h has no key 3, h[3] on the right side equals nil. Continuing,
#=> (h[3] = nil || []) << [0, 5, 14]
#=> (h[3] = []) << [0, 5, 14]
h #=> { 3=>[[0, 5, 14]] }
Notice that we throw away scan's return value. All we need is b
This tells us the "ABBA" appears thrice in str, beginning at offsets 0, 5 and 14.
Now observe
e.to_a
#=> [[0, [[0, 5, 14]]], [1, [[0, 5, 14]]], [2, [[0, 5, 14]]],
# ...
# [16, [[0, 5, 14]]]]
After all elements of e have been passed to the block, the block returns
h #=> {3=>[[0, 5, 14], [1, 6, 15]],
# 1=>[[2], [3], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16]],
# 2=>[[4, 16], [5, 14], [6, 15]]}
Consider substrings that appear just once: h[1]. One of those is [2]. This pertains to the 4-character substring beginning at str offset 2:
str[2,4]
#=> "BABA"
That is found to be the only instance of that substring. Similarly, among the substrings that appear twice is str[4,4] = str[16,4] #=> "BABB", given by h[2][0] #=> [4, 16].
Next we determine the greatest frequency of a substring of length 4:
c = h.max_by(&:first)
#=> [3, [[0, 5, 14], [1, 6, 15]]]
(which could also be written c = h.max_by { |k,_| k }).
d = c.last
#=> [[0, 5, 14], [1, 6, 15]]
For convenience, convert d to a hash:
d.each_with_object({}) { |a,h| h[str[a.first,k]] = a }
#=> {"ABBA"=>[0, 5, 14], "BBAB"=>[1, 6, 15]}
and return that hash from the method.
There is one detail that deserves mention. It is possible that d will contain two or more arrays that reference the same substring, in which case the value of the associated key (the substring) will equal the last of those arrays. Here's a simple example.
str = "AAA"
k = 2
In this case the array d above will equal
d = [[0], [1]]
Both of these reference str[0,2] #=> str[1,2] #=> "AA". In building the hash the first is overwritten by the second:
d.each_with_object({}) { |a,h| h[str[a.first,k]] = a }
#=> {"AA"=>[1]}

leetcode first (the easiest) - two_sum

I wanted to practise some algorithms... Why doesn't my solution work on leetcode website?!?!
PS: Would be grateful for other resources to learn algorithms and practise interview questions.
# #param {Integer[]} nums
# #param {Integer} target
# #return {Integer[]}
def two_sum(nums, target)
i,j = 0,nums.length-1
output = []
while i < nums.length-1
while j > i
if nums[i] + nums[j] == target
output << i << j
end
j-=1
end
i+=1
end
output
end
Result from the website:
Input:
[3,2,4]
6
Output: []
Expected:[1,2]
Now that your question has been answered, I would like to suggest a more Ruby-like method.
Code
def two_sum(nums, target)
(0...nums.size).to_a.combination(2).find { |i,j| nums[i]+nums[j] == target }
end
Example
nums = [1,5,2,3,4]
target = 8
two_sum(nums, target)
#=> [1,3]
Explanation
For the example above, the steps are as follows:
a = nums.size
#=> 5
b = a.times
#=> #<Enumerator: 5:times>
c = b.to_a
#=> [0, 1, 2, 3, 4]
d = c.combination(2)
#=> #<Enumerator: [0, 1, 2, 3, 4]:combination(2)>
We can see the elements that are generated by the enumerator d by converting it to an array.
d.to_a
#=> [[0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
d.find { |i,j| nums[i]+nums[j] == target }
#=> [1, 3]
Note that (0...nums.size).to_a could be replaced by any of the following.
[*0...nums.size]
nums.each_index.to_a
nums.size.times.to_a
0.upto(nums.size-1).to_a
Array.new(nums.size) { |i| i }
Array.new(nums.size, &:itself)
Your error is that you don't reinitialize j when it reaches i which means that your algorithm just tries (0,n-1), (0,n-2), …, (0, 2), (0, 1) and then stops.

Find combinations in Ruby that are less than a certain number

Say I have an array [1,2,3] and I want every combination of these numbers that don't exceed 4. So I would have [1,2,3].someMethod(4) and it would give me:
[1,1,1,1]
[1,1,2]
[1,3]
[2,2]
So far I have:
(1..4).flat_map{|size| [1,2,3].repeated_combination(size).to_a }
but this gives me every possible combinations, including the ones that exceed my given limit. Is there an good way to either only get combinations that add up to my limit?
arr = [1,2,3]
(arr+[0]).repeated_combination(4).select { |a| a.reduce(:+) == 4 }.map { |a| a - [0] }
#=> [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Change == to <= if desired.
This answer, like the others, assumes arr contains natural numbers, including 1.
results = (1..4).each.with_object([]) do |size, results|
[1,2,3].repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
p results
--output:--
[[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Parameterizing the algorithm:
def do_stuff(values, target_total)
(1..target_total).each.with_object([]) do |size, results|
values.repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
end
p do_stuff([1, 2, 3], 4)
You can filter out the arrays you don't want by using the select method. Just select all the arrays that have a sum == 4 (the sum is calculated by the inject method).
all_arrs = (1..4).flat_map do |size|
[1,2,3].repeated_combination(size).to_a
end
valid_arrs = all_arrs.select do |arr|
arr.inject { |a, b| a + b } == 4
end
print valid_arrs
# Output:
# [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
A recursive approach.
def some_method(a, n)
return [[]] if n == 0
a.select { |e| e <= n }.\
flat_map { |e| some_method(a,n-e).map { |es| ([e] + es).sort } }.\
sort.\
uniq
end
p some_method([1,2,3], 4)
# => [[1, 1, 1, 1], [1, 1, 2], [1, 3], [2, 2]]
EDIT: Here is another recursive version without filtering duplicates but with opposite order. I added comments to make it clearer.
def some_method(a, n)
return [[]] if n == 0 # bottom (solution) found
return [] if a.empty? || n < 0 # no solution
max = a.max
# search all solutions with biggest value
l = some_method(a, n-max).map { |e| [max] + e }
# search all solutions without biggest value
r = some_method(a-[max],n)
l + r
end
p some_method([1,2,3], 4)
# => [[3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]

Identify runs on array with ruby

If we have an array
array = [1, 1, 0, 0, 2, 3, 0, 0, 0, 3, 3, 3 ]
How can we identify the run (amount of consecutive numbers with same value) of a given number?
By example:
run_pattern_for(array, 0) -> 2
run_pattern_for(array, 3) -> 1
run_pattern_for(array, 1) -> 1
run_pattern_for(array, 2) -> 0
There are no runs for 2 because there are no consecutive apparitions of two.
There are one run for 3 because there are only one apparition with the tree as consecutive numbers.
try:
class Array
def count_runs(element)
chunk {|n| n}.count {|a,b| a == element && b.length > 1}
end
end
a = [1, 1, 0, 0, 2, 3, 0, 0, 0, 3, 3, 3 ]
a.count_runs 0 #=> 2
a.count_runs 3 #=> 1
a.count_runs 1 #=> 1
a.count_runs 2 #=> 0
I agree with #BroiSatse that Enumerable#chunk should be used here, but I would like to show how an enumerator could be employed directly to solve this problem, using the methods Enumerator#next and Enumerator#peek.
Code
def count_em(array)
return [] if array.empty?
h = Hash.new(0)
enum = array.each
loop do
x = enum.next
if x == enum.peek
h[x] += 1
enum.next until (enum.peek != x)
else
h[x] = 0 unless h.key?(x)
end
end
h
end
Example
array = [1, 1, 0, 0, 2, 3, 0, 0, 0, 3, 3, 3 ]
count_em(array) #=> {1=>1, 0=>2, 2=>0, 3=>1}
Explanation
Suppose
array = [1, 1, 1, 0, 2, 2]
h = Hash.new(0)
enum = array.each
#=> #<Enumerator: [1, 1, 1, 0, 2, 2]:each>
x = enum.next #=> 1
enum.peek #=> 1
so x == enum.peek #=> true, meaning there is a run of at least two 1's, so wish execute:
h[x] += 1 #=> h[1] += 1
which means
h[1] = h[1] + 1
Since h does not have a key 1, h[x] on the right side of the equality set to zero, the default value we established when creating the hash. Therefore, the hash h is now { 1=>1 }. Now we want need to enumerate and discard any more 1's in the run:
enum.next until (enum.peek != x)
enum.next #=> 1
enum.peek #=> 1
enum.next #=> 1
enum.peek #=> 0
Now go back to the top of the loop:
x = enum.next #=> 0
enum.peek #=> 2
Since (x == enum.peek) => (0 == 2) => false, and h.key?(x) => false, we set
h[0] = 0
and the hash is now { 1=>1, 0=>0 }. Returning again to the top of the loop,
x = enum.next #=> 2
enum.peek #=> 2
Since (x == enum.peek) => (2 == 2) => true, we execute:
h[2] += 1 #=> 1
so now h => {1=>1, 0=>0, 2=>1}. Now when we execute
x = enum.next #=> 2
enum.peek #=> StopIteration: iteration reached an end
The exception is rescued by Kernel#loop. That is, raising a StopIteration error is one way to break out of the loop, causing the last line of the method to be executed and returned:
h #=> {1=>1, 0=>0, 2=>1}
(Note this result differs from that in the example above because it is for a different array.)
Ruby 2.2, which was released roughly seven months after this question was posted, gave us a method that has application here, Enumerable#slice_when:
array.slice_when { |i,j| i != j }.each_with_object(Hash.new(0)) { |a,h|
h[a.first] += (a.size > 1) ? 1 : 0 }
#=> {1=>1, 0=>2, 2=>0, 3=>1}
It's a simple task; Here are two different ways I've done it:
array = [1, 1, 0, 0, 2, 3, 0, 0, 0, 3, 3, 3 ]
hash = Hash[array.group_by { |e| e }.map{ |k, v| [k, v.size] }]
# => {1=>2, 0=>5, 2=>1, 3=>4}
And:
hash = Hash.new{ |h,k| h[k] = 0 }
array.each { |e| hash[e] += 1 }
hash # => {1=>2, 0=>5, 2=>1, 3=>4}
Once you have the hash the rest is easy:
hash[0] # => 5
hash[1] # => 2
hash[2] # => 1
hash[3] # => 4
If it's possible you'll request a count for a number that didn't exist in the array, and want a numeric response instead of nil, use something like:
Integer(hash[4]) # => 0
Integer(...) converts nil to 0 for you.
In the first example above, group_by will do the heavy lifting, and results in:
array.group_by { |e| e } # => {1=>[1, 1], 0=>[0, 0, 0, 0, 0], 2=>[2], 3=>[3, 3, 3, 3]}
The map statement simply converts the array to its size.

Resources