Counting Frequency of Values in an Array (And Sorting by Frequency?)

Is there no method for an array that will tell me the # of occurrences
for an item?

IE: ["a", "a", "a", "b", "c", "c"].count("a") #producing 3 ?

I almost thought that rindex would do the trick when looking at the
class docs but.. the example was just engineered to trick me :frowning:

I realize I could pass these to a block and count but.. wanted to make
sure it didn't exist. If not, why? Thank you.. ( I did search btw.. no
avail )

Also, what's the best way of printing out each unique item and the
number of times it occurs, sorted by numerically by the number of
times it occurs?

IE: in my example above, i'd like to see (sorted by occurrence
greatest to least)
#desired output:

路路路

a: 3
c: 2
b: 1

Or sorted from least to greatest:
#desired output:
b: 1
c: 2
a: 3

I was able to hack it by using a hash doing various things to it.. but
it didn't seem "rubyish".

Thank you for any input.

x1 wrote:

I was able to hack it by using a hash doing various things to it.. but
it didn't seem "rubyish".

What could be more ruby-ish than monkeypatching a built-in class and
using inject in the process?

class Array
  def counts
    inject( Hash.new(0) ){ |hash,element|
      hash[ element ] +=1
      hash
    }
  end
  def counts_up
    counts.sort_by{ |k,v| v }
  end
  def counts_down
    counts.sort_by{ |k,v| -v }
  end
end
a = ["a", "a", "a", "b", "c", "c"]
p a.counts, a.counts_up, a.counts_down
#=> {"a"=>3, "b"=>1, "c"=>2}
#=> [["b", 1], ["c", 2], ["a", 3]]
#=> [["a", 3], ["c", 2], ["b", 1]]

Is there no method for an array that will tell me the # of occurrences
for an item?

IE: ["a", "a", "a", "b", "c", "c"].count("a") #producing 3 ?

array.select { |i| i == 'a' }.length
or array.inject(0) { |count, item| count += 1 if item == 'a'; count }

I almost thought that rindex would do the trick when looking at the
class docs but.. the example was just engineered to trick me :frowning:

I realize I could pass these to a block and count but.. wanted to make
sure it didn't exist. If not, why? Thank you.. ( I did search btw.. no
avail )

Also, what's the best way of printing out each unique item and the
number of times it occurs, sorted by numerically by the number of
times it occurs?

IE: in my example above, i'd like to see (sorted by occurrence
greatest to least)
#desired output:
a: 3
c: 2
b: 1

Or sorted from least to greatest:
#desired output:
b: 1
c: 2
a: 3

I was able to hack it by using a hash doing various things to it.. but
it didn't seem "rubyish".

As you say:
puts array.inject(Hash.new(0)) { |hash, item| hash[item] += 1
  hash }.sort_by { |k, v| v }.map { |k, v| "#{k}:#{v}" }

路路路

On Thu, Oct 12, 2006 at 11:52:19AM +0900, x1 wrote:

Thank you for any input.

Hi,

At Thu, 12 Oct 2006 11:52:19 +0900,
x1 wrote in [ruby-talk:219218]:

Is there no method for an array that will tell me the # of occurrences
for an item?

IE: ["a", "a", "a", "b", "c", "c"].count("a") #producing 3 ?

FYI, Enumerable in 1.9 has that method.

路路路

--
Nobu Nakada

Hi,

From: x1 <caldridge@gmail.com>
Reply-To: ruby-talk@ruby-lang.org
To: ruby-talk@ruby-lang.org (ruby-talk ML)
Subject: Counting Frequency of Values in an Array (And Sorting by Frequency?)
Date: Thu, 12 Oct 2006 11:52:19 +0900

Is there no method for an array that will tell me the # of occurrences
for an item?

IE: ["a", "a", "a", "b", "c", "c"].count("a") #producing 3 ?

You can use ["a", "a", "a", "b", "c", "c"].grep("a").size

I almost thought that rindex would do the trick when looking at the
class docs but.. the example was just engineered to trick me :frowning:

I realize I could pass these to a block and count but.. wanted to make
sure it didn't exist. If not, why? Thank you.. ( I did search btw.. no
avail )

Also, what's the best way of printing out each unique item and the
number of times it occurs, sorted by numerically by the number of
times it occurs?

IE: in my example above, i'd like to see (sorted by occurrence
greatest to least)
#desired output:
a: 3
c: 2
b: 1

array.uniq.sort_by{|x|array.grep(x).size}.reverse.each{|x|puts "#{x}: #{array.grep(x).size}"}

Or sorted from least to greatest:
#desired output:
b: 1
c: 2
a: 3

array.uniq.sort_by{|x|array.grep(x).size}.each{|x|puts "#{x}: #{array.grep(x).size}"}

Regards,
Park Heesob

Smalltalk has a collection class called Bag, which is an unordered
collection of objects which keeps track of the number of occurences of
each equal element in the collection, so you can do something like:

   bag <- Bag.new
   bag.add: 1;add: 2: add: 1; add: 4
   bag.occurencesOf: 1 => 2
   bag.occurencesOf: 1 => 1
   bag.remove: 1
   bag.occurrencesOf: 1 => 1

Now Bag is kind of the black sheep of the Smalltalk collection
classes. Most Smalltalkers would either never use it or overuse it.
The only use I could think of was to implement a histogram.

路路路

On 10/11/06, x1 <caldridge@gmail.com> wrote:

Is there no method for an array that will tell me the # of occurrences
for an item?

IE: ["a", "a", "a", "b", "c", "c"].count("a") #producing 3 ?

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Wow Capaldo, that worked! I fear for the next java programmer who has
to make sense of my code when I leave :wink:

To reverse sort, I added .reverse.. Here's the final product:
puts ["a", "a", "a", "b", "c", "c"].inject(Hash.new(0)) { |hash, item|
hash[item] += 1
hash }.sort_by { |k, v| v }.reverse.map { |k, v| "#{k}:#{v}" }

Thanks so much.

Nakada, I look forward to using it in 1.9 :slight_smile:

路路路

On 10/11/06, Nobuyoshi Nakada <nobu@ruby-lang.org> wrote:

Hi,

At Thu, 12 Oct 2006 11:52:19 +0900,
x1 wrote in [ruby-talk:219218]:
> Is there no method for an array that will tell me the # of occurrences
> for an item?
>
> IE: ["a", "a", "a", "b", "c", "c"].count("a") #producing 3 ?

FYI, Enumerable in 1.9 has that method.

--
Nobu Nakada

and.. so you're aware.. my hacky code was something like this:

items = {}
["a", "a", "a", "b", "c", "c"].each do |i|
   if items.include? i
     items[i] += 1
   else
     items[i] = 1
   end
end

items.sort {|a,b| a[1]<=>b[1]}.reverse.each do |a, b|
  puts a + ":" + b.to_s
end

horrific eh?

路路路

On 10/11/06, x1 <caldridge@gmail.com> wrote:

Wow Capaldo, that worked! I fear for the next java programmer who has
to make sense of my code when I leave :wink:

To reverse sort, I added .reverse.. Here's the final product:
puts ["a", "a", "a", "b", "c", "c"].inject(Hash.new(0)) { |hash, item|
hash[item] += 1
hash }.sort_by { |k, v| v }.reverse.map { |k, v| "#{k}:#{v}" }

Thanks so much.

Nakada, I look forward to using it in 1.9 :slight_smile:

On 10/11/06, Nobuyoshi Nakada <nobu@ruby-lang.org> wrote:
> Hi,
>
> At Thu, 12 Oct 2006 11:52:19 +0900,
> x1 wrote in [ruby-talk:219218]:
> > Is there no method for an array that will tell me the # of occurrences
> > for an item?
> >
> > IE: ["a", "a", "a", "b", "c", "c"].count("a") #producing 3 ?
>
> FYI, Enumerable in 1.9 has that method.
>
> --
> Nobu Nakada
>

items = Hash.new(0)
["a","a","a","b","c","c"].each do |i|
  items[i] += 1
end
items.sort_by {|key,value| -value}.each do |key, value|
  puts "#{a}:#{b}"
end

Slightly less horrific?

路路路

and.. so you're aware.. my hacky code was something like this:

items = {}
["a", "a", "a", "b", "c", "c"].each do |i|
   if items.include? i
     items[i] += 1
   else
     items[i] = 1
   end
end

items.sort {|a,b| a[1]<=>b[1]}.reverse.each do |a, b|
  puts a + ":" + b.to_s
end

horrific eh?

Ah ok.. I'm with ya. Meet "sort_by", my new friend. Thanks again :smiley:

路路路

On 10/12/06, Daniel Sheppard <daniels@pronto.com.au> wrote:

items = Hash.new(0)
["a","a","a","b","c","c"].each do |i|
        items[i] += 1
end
items.sort_by {|key,value| -value}.each do |key, value|
        puts "#{a}:#{b}"
end

Slightly less horrific?

> and.. so you're aware.. my hacky code was something like this:
>
> items = {}
> ["a", "a", "a", "b", "c", "c"].each do |i|
> if items.include? i
> items[i] += 1
> else
> items[i] = 1
> end
> end
>
> items.sort {|a,b| a[1]<=>b[1]}.reverse.each do |a, b|
> puts a + ":" + b.to_s
> end
>
> horrific eh?