How many times is item in array? [duplicate] - ruby

I have an array:
a = [1, 2, 3, 3, 6, 8, 1, 9]
I want to display each unique element value and its associated element count like this:
1: 2
2: 1
3: 2
6: 1
8: 1
9: 1
So far I have:
a.sort.group_by { |x| x }
{
1 => [
[0] 1,
[1] 1
],
2 => [
[0] 2
],
3 => [
[0] 3,
[1] 3
],
6 => [
[0] 6
],
8 => [
[0] 8
],
9 => [
[0] 9
]
}
So each element of the Hash contains an array. I can use that array's count to get my answer, but I'm having trouble figuring out how to process the hash concisely.
Is this a horrible implementation?
a.sort.group_by { |x| x }.each {|x| puts "#{x[0]} #{x[1].count}" }

How about:
a.inject({}) { |a,e| a[e] = (a[e] || 0) + 1; a }
=> {1=>2, 2=>1, 3=>2, 6=>1, 8=>1, 9=>1}
For example:
h = a.inject({}) { |a,e| a[e] = (a[e] || 0) + 1; a }
=> {1=>2, 2=>1, 3=>2, 6=>1, 8=>1, 9=>1}
h.keys.sort.each { |k| puts "#{k}: #{h[k]}" }
1: 2
2: 1
3: 2
6: 1
8: 1
9: 1
From comments of others below:
a.each_with_object(Hash.new(0)) { |e,a| a[e] += 1 }
=> {1=>2, 2=>1, 3=>2, 6=>1, 8=>1, 9=>1}

Use uniq to get the unique array values and sort to sort them in ascending order. Then for each of these values x, display a.count(x).
a = [1, 2, 3, 3, 6, 8, 1, 9]
a.uniq.sort.each {|x| puts '%d: %d' % [x, a.count(x)] }
For greater efficiency, make a hash that maps a value to the number of times it appears in the array. An easy way to do this is to initialize a Hash object that maps keys to zero by default. Then you can increment each value's count by one as you iterate through the array.
counts = Hash.new(0)
a.each {|x| counts[x] += 1 }
counts.keys.sort.each {|x| puts '%d: %d' % [x, counts[x]] }

Consider this:
a = [1, 2, 3, 3, 6, 8, 1, 9]
a.group_by{ |n| n } # => {1=>[1, 1], 2=>[2], 3=>[3, 3], 6=>[6], 8=>[8], 9=>[9]}
a.group_by{ |n| n }.map{ |k, v| [k, v.size ] } # => [[1, 2], [2, 1], [3, 2], [6, 1], [8, 1], [9, 1]]
Finally:
a.group_by{ |n| n }.map{ |k, v| [k, v.size ] }.to_h # => {1=>2, 2=>1, 3=>2, 6=>1, 8=>1, 9=>1}

Try this
module Enumerable
def freq
hash = Hash.new(0)
each { |each| hash[each] += 1 }
hash
end
end
And then
[1, 2, 3, 3, 6, 8, 1, 9].freq
# => {1=>2, 2=>1, 3=>2, 6=>1, 8=>1, 9=>1}

Related

How to instantiate a multilevel hash with an arbitrary depth

I want to create a hash from the result of a database query such that its keys are the column values except the last one, and the value are the last column value (or a default value). For example, if I have rows:
1 2 3 1
1 2 4 9
1 3 2 nil
and a default value of 111, I should get:
{
1 =>
{
2 => { 3 => 1, 4 => 9},
3 => { 2 => 111}
}
}
I want to make the method generic enough to handle an arbitrary number of columns, so the signature could be:
to_lookup(rows, default_value, value_column, *columns)
How would I go about that?
Update: forgot a comma in the output.
[Edit: after reading #cthulhu's answer, I think I may have misinterpreted the question. I assumed that consecutive rows were to be grouped, rather than all rows to be grouped. I will leave my answer for the former interpretation.]
I believe this is what you are looking for:
def hashify(arr)
return arr.first.first if arr.first.size == 1
arr.slice_when { |f,s| f.first != s.first }.
each_with_object({}) do |a,h|
key, *rest = a.transpose
h[key.first] = hashify(rest.transpose)
end
end
hashify [[1, 2, 3, 1], [1, 2, 4, 9], [1, 3, 2, nil]]
#=> {1=>{2=>{3=>1, 4=>9}, 3=>{2=>nil}}}
hashify [[1, 2, 3, 1], [1, 2, 4, 9], [2, 3, 2, nil]]
#=> {1=>{2=>{3=>1, 4=>9}}, 2=>{3=>{2=>nil}}}
Replacing nil with the default can be done before or after the construction of the hash.
Enumerable#slice_when was bestowed upon us in v2.2. For earlier versions, you could replace:
arr.slice_when { |f,s| f.first != s.first }
with
arr.chunk { |row| row.first }.map(&:last)
I simplified things by removing the ability to pass a default,
I also simplified the signature method to have only one parameter.
RSpec.describe "#to_lookup" do
def to_lookup(rows)
return rows.first.first if rows.flatten.size == 1
h = {}
rows.group_by { |e| e.first }.each_entry do |k, v|
v.each &:shift
h[k] = to_lookup(v)
end
h
end
let :input do
[
[1, 2, 3, 1],
[1, 2, 4, 9],
[1, 3, 2, 111],
]
end
let :output do
{
1 => {
2 => {3 => 1, 4 => 9},
3 => {2 => 111}
}
}
end
it { expect(to_lookup(input)).to eq(output) }
end
BTW I wonder what output do you want for following input:
1 2 3 1
1 2 3 2
EDIT: working code snippet: http://rubysandbox.com/#/snippet/566aefa80195f1000c000000

Get items from Ruby Array that occur 2 or more times

Let's say I have a Ruby array.
[1,2,3,4,4,5,6,6,7,7]
I want to find the values that occur 2 or more times.
[4,6,7]
It will help my process to first determine which items occur only once then remove those. So I'd like to solve this by first finding the items that occur once.
There are probably better ways, but this is one:
> [1,2,3,4,4,5,6,6,7,7].group_by{|i| i}.reject{|k,v| v.size == 1}.keys
=> [4, 6, 7]
Breaking it down:
> a = [1,2,3,4,4,5,6,6,7,7]
=> [1, 2, 3, 4, 4, 5, 6, 6, 7, 7]
> a1 = a.group_by{|i| i}
=> {1=>[1], 2=>[2], 3=>[3], 4=>[4, 4], 5=>[5], 6=>[6, 6], 7=>[7, 7]}
> a2 = a1.reject{|k,v| v.size == 1}
=> {4=>[4, 4], 6=>[6, 6], 7=>[7, 7]}
> a2.keys
=> [4, 6, 7]
Everyone loves a really difficult to follow one liner :)
[1,2,3,4,4,5,6,6,7,7].each_with_object(Hash.new(0)) { |o, h| h[o] += 1 }.select { |_, v| v > 1 }.keys
Add some white space and some comments
[1,2,3,4,4,5,6,6,7,7].each_with_object(Hash.new(0)) { |o, h|
h[o] += 1
}.select { |_, v|
v > 1
}.keys
Enumerate and pass in our memo hash to each iteration the Hash defaults to having 0 for any key
Increment counter for the object
Select only key value pairs where the value is greater than 1
Grab just the keys
This looks quite similar to Phillip's neat answer - in theory this should use slightly less memory as it will not have to build the intermediate arrays to perform counting
Another way:
a = [1,2,3,4,4,5,6,6,7,7]
au = a.uniq
a.reject { |i| au.delete(i) }
#=> [4, 6, 7]
If efficiency is important, you could use a set:
require 'set'
s = Set.new
a.reject { |e| s.add?(e) }
#=> [4, 6, 7]
You can use Array#select to return the elements where Array#count is greater than 1:
2.1.2 :005 > arr = [1,2,3,4,4,5,6,6,7,7]
=> [1, 2, 3, 4, 4, 5, 6, 6, 7, 7]
2.1.2 :006 > arr.select { |e| arr.count(e) > 1 }.uniq
=> [4, 6, 7]
Hope this helps

Ruby: Sum selected hash values

I've got an array of hashes and would like to sum up selected values. I know how to sum all of them or one of them but not how to select more than one key.
i.e.:
[{"a"=>5, "b"=>10, "active"=>"yes"}, {"a"=>5, "b"=>10, "active"=>"no"}, {"a"=>5, "b"=>10, "action"=>"yes"}]
To sum all of them I using:
t = h.inject{|memo, el| memo.merge( el ){|k, old_v, new_v| old_v + new_v}}
=> {"a"=>15, "b"=>30, "active"=>"yesnoyes"} # I do not want 'active'
To sum one key, I do:
h.map{|x| x['a']}.reduce(:+)
=> 15
How do I go about summing up values for keys 'a' and 'b'?
You can use values_at:
hs = [{:a => 1, :b => 2, :c => ""}, {:a => 2, :b => 4, :c => ""}]
keys = [:a, :b]
hs.map { |h| h.values_at(*keys) }.inject { |a, v| a.zip(v).map { |xy| xy.compact.sum }}
# => [3, 6]
If all required keys have values it will be shorter:
hs.map { |h| h.values_at(*keys) }.inject { |a, v| a.zip(v).map(&:sum) }
# => [3, 6]
If you want Hash back:
Hash[keys.zip(hs.map { |h| h.values_at(*keys) }.inject{ |a, v| a.zip(v).map(&:sum) })]
# => {:a => 3, :b => 6}
I'd do something like this:
a.map { |h| h.values_at("a", "b") }.transpose.map { |v| v.inject(:+) }
#=> [15, 30]
Step by step:
a.map { |h| h.values_at("a", "b") } #=> [[5, 10], [5, 10], [5, 10]]
.transpose #=> [[5, 5, 5], [10, 10, 10]]
.map { |v| v.inject(:+) } #=> [15, 30]
How is this ?
h = [{"a"=>5, "b"=>10, "active"=>"yes"}, {"a"=>5, "b"=>10, "active"=>"no"}, {"a"=>5, "b"=>10, "action"=>"yes"}]
p h.map{|e| e.reject{|k,v| %w(active action).include? k } }.inject{|memo, el| memo.merge( el ){|k, old_v, new_v| old_v + new_v}}
# >> {"a"=>15, "b"=>30}

How to quickly print Ruby hashes in a table format?

Is there a way to quickly print a ruby hash in a table format into a file?
Such as:
keyA keyB keyC ...
123 234 345
125 347
4456
...
where the values of the hash are arrays of different sizes. Or is using a double loop the only way?
Thanks
Try this gem I wrote (prints hashes, ruby objects, ActiveRecord objects in tables): http://github.com/arches/table_print
Here's a version of steenslag's that works when the arrays aren't the same size:
size = h.values.max_by { |a| a.length }.length
m = h.values.map { |a| a += [nil] * (size - a.length) }.transpose.insert(0, h.keys)
nil seems like a reasonable placeholder for missing values but you can, of course, use whatever makes sense.
For example:
>> h = {:a => [1, 2, 3], :b => [4, 5, 6, 7, 8], :c => [9]}
>> size = h.values.max_by { |a| a.length }.length
>> m = h.values.map { |a| a += [nil] * (size - a.length) }.transpose.insert(0, h.keys)
=> [[:a, :b, :c], [1, 4, 9], [2, 5, nil], [3, 6, nil], [nil, 7, nil], [nil, 8, nil]]
>> m.each { |r| puts r.map { |x| x.nil?? '' : x }.inspect }
[:a, :b, :c]
[ 1, 4, 9]
[ 2, 5, ""]
[ 3, 6, ""]
["", 7, ""]
["", 8, ""]
h = {:a => [1, 2, 3], :b => [4, 5, 6], :c => [7, 8, 9]}
p h.values.transpose.insert(0, h.keys)
# [[:a, :b, :c], [1, 4, 7], [2, 5, 8], [3, 6, 9]]
No, there's no built-in function. Here's a code that would format it as you want it:
data = { :keyA => [123, 125, 4456], :keyB => [234000], :keyC => [345, 347] }
length = data.values.max_by{ |v| v.length }.length
widths = {}
data.keys.each do |key|
widths[key] = 5 # minimum column width
# longest string len of values
val_len = data[key].max_by{ |v| v.to_s.length }.to_s.length
widths[key] = (val_len > widths[key]) ? val_len : widths[key]
# length of key
widths[key] = (key.to_s.length > widths[key]) ? key.to_s.length : widths[key]
end
result = ""
data.keys.each {|key| result += key.to_s.ljust(widths[key]) + " " }
result += "\n"
for i in 0.upto(length)
data.keys.each { |key| result += data[key][i].to_s.ljust(widths[key]) + " " }
result += "\n"
end
# TODO write result to file...
Any comments and edits to refine the answer are very welcome.

How to count duplicates in Ruby Arrays

How do you count duplicates in a ruby array?
For example, if my array had three a's, how could I count that
Another version of a hash with a key for each element in your array and value for the count of each element
a = [ 1, 2, 3, 3, 4, 3]
h = Hash.new(0)
a.each { | v | h.store(v, h[v]+1) }
# h = { 3=>3, 2=>1, 1=>1, 4=>1 }
Given:
arr = [ 1, 2, 3, 2, 4, 5, 3]
My favourite way of counting elements is:
counts = arr.group_by{|i| i}.map{|k,v| [k, v.count] }
# => [[1, 1], [2, 2], [3, 2], [4, 1], [5, 1]]
If you need a hash instead of an array:
Hash[*counts.flatten]
# => {1=>1, 2=>2, 3=>2, 4=>1, 5=>1}
This will yield the duplicate elements as a hash with the number of occurences for each duplicate item. Let the code speak:
#!/usr/bin/env ruby
class Array
# monkey-patched version
def dup_hash
inject(Hash.new(0)) { |h,e| h[e] += 1; h }.select {
|k,v| v > 1 }.inject({}) { |r, e| r[e.first] = e.last; r }
end
end
# unmonkeey'd
def dup_hash(ary)
ary.inject(Hash.new(0)) { |h,e| h[e] += 1; h }.select {
|_k,v| v > 1 }.inject({}) { |r, e| r[e.first] = e.last; r }
end
p dup_hash([1, 2, "a", "a", 4, "a", 2, 1])
# {"a"=>3, 1=>2, 2=>2}
p [1, 2, "Thanks", "You're welcome", "Thanks",
"You're welcome", "Thanks", "You're welcome"].dup_hash
# {"You're welcome"=>3, "Thanks"=>3}
Simple.
arr = [2,3,4,3,2,67,2]
repeats = arr.length - arr.uniq.length
puts repeats
arr = %w( a b c d c b a )
# => ["a", "b", "c", "d", "c", "b", "a"]
arr.count('a')
# => 2
Another way to count array duplicates is:
arr= [2,2,3,3,2,4,2]
arr.group_by{|x| x}.map{|k,v| [k,v.count] }
result is
[[2, 4], [3, 2], [4, 1]]
requires 1.8.7+ for group_by
ary = %w{a b c d a e f g a h i b}
ary.group_by{|elem| elem}.select{|key,val| val.length > 1}.map{|key,val| key}
# => ["a", "b"]
with 1.9+ this can be slightly simplified because Hash#select will return a hash.
ary.group_by{|elem| elem}.select{|key,val| val.length > 1}.keys
# => ["a", "b"]
To count instances of a single element use inject
array.inject(0){|count,elem| elem == value ? count+1 : count}
arr = [1, 2, "a", "a", 4, "a", 2, 1]
arr.group_by(&:itself).transform_values(&:size)
#=> {1=>2, 2=>2, "a"=>3, 4=>1}
Ruby >= 2.7 solution here:
A new method .tally has been added.
Tallies the collection, i.e., counts the occurrences of each element. Returns a hash with the elements of the collection as keys and the corresponding counts as values.
So now, you will be able to do:
["a", "b", "c", "b"].tally #=> {"a"=>1, "b"=>2, "c"=>1}
What about a grep?
arr = [1, 2, "Thanks", "You're welcome", "Thanks", "You're welcome", "Thanks", "You're welcome"]
arr.grep('Thanks').size # => 3
Its Easy:
words = ["aa","bb","cc","bb","bb","cc"]
One line simple solution is:
words.each_with_object(Hash.new(0)) { |word,counts| counts[word] += 1 }
It works for me.
Thanks!!
I don't think there's a built-in method. If all you need is the total count of duplicates, you could take a.length - a.uniq.length. If you're looking for the count of a single particular element, try
a.select {|e| e == my_element}.length.
Improving #Kim's answer:
arr = [1, 2, "a", "a", 4, "a", 2, 1]
Hash.new(0).tap { |h| arr.each { |v| h[v] += 1 } }
# => {1=>2, 2=>2, "a"=>3, 4=>1}
Ruby code to get the repeated elements in the array:
numbers = [1,2,3,1,2,0,8,9,0,1,2,3]
similar = numbers.each_with_object([]) do |n, dups|
dups << n if seen.include?(n)
seen << n
end
print "similar --> ", similar
Another way to do it is to use each_with_object:
a = [ 1, 2, 3, 3, 4, 3]
hash = a.each_with_object({}) {|v, h|
h[v] ||= 0
h[v] += 1
}
# hash = { 3=>3, 2=>1, 1=>1, 4=>1 }
This way, calling a non-existing key such as hash[5] will return nil instead of 0 with Kim's solution.
I've used reduce/inject for this in the past, like the following
array = [1,5,4,3,1,5,6,8,8,8,9]
array.reduce (Hash.new(0)) {|counts, el| counts[el]+=1; counts}
produces
=> {1=>2, 5=>2, 4=>1, 3=>1, 6=>1, 8=>3, 9=>1}

Resources