Count the number of times an element occurs in an array

Discussion in 'Ruby' started by Jim Burgess, Oct 5, 2009.

  1. Jim Burgess

    Jim Burgess Guest

    Hi,

    I need to count the number of times an element occurs in an array.

    At the moment I'm doing it like this:

    a = ["a", "b", "a", "b", "b"]
    b = a.uniq
    c = []

    0.upto(b.length-1) do |e|
    d = 0
    0.upto(a.length-1) do |f|
    if b[e] == a[f]
    d += 1
    end
    end
    c << d
    end

    print b, c
    > ["a", "b"] [2, 3]


    This works, but seems really clumsy.
    Can anyone give me any tips on what I could improve?
    Cheers.
    --
    Posted via http://www.ruby-forum.com/.
     
    Jim Burgess, Oct 5, 2009
    #1
    1. Advertising

  2. Jim Burgess

    Ryan Davis Guest

    On Oct 5, 2009, at 02:24 , Jim Burgess wrote:

    > Hi,
    >
    > I need to count the number of times an element occurs in an array.
    >
    > At the moment I'm doing it like this:
    >
    > a = ["a", "b", "a", "b", "b"]
    > b = a.uniq
    > c = []
    >
    > 0.upto(b.length-1) do |e|
    > d = 0
    > 0.upto(a.length-1) do |f|
    > if b[e] == a[f]
    > d += 1
    > end
    > end
    > c << d
    > end
    >
    > print b, c
    >> ["a", "b"] [2, 3]


    Some suggestions:

    Your indentation is wack. Make sure you're not mixing spaces and tabs.
    2 spaces per indent. You're mixing 2 & 3 & possibly tabs at 8.

    Your variable names are meaningless. Improve that and you'll have a
    lot better time debugging when it is 4am and you're tired.

    You're not using (sensible) iterators. I suggest you read that section
    in the pickaxe book.

    You're not using good data structures or algorithms. You're passing
    over the elements of 'a' (see? better names means better readability!)
    a.uniq.size times and doing a.size * a.uniq.size string equality
    comparisons.

    Take a look at this:

    # use a hash whose values default to 0 for proper data structure use
    # use count as the name, since that's what it is storing.
    count = Hash.new 0

    # iterate over each string only once
    strings.each do |string|
    # increment the count for each string
    count[string] += 1
    end

    p count

    If you nuke all the comments, the code makes just as much sense (if
    not more). That's what we try to achieve in ruby whenever we can.
     
    Ryan Davis, Oct 5, 2009
    #2
    1. Advertising

  3. Hello,

    2009/10/5 Jim Burgess <>:
    > Hi,
    >
    > I need to count the number of times an element occurs in an array.


    Using Enumerable.count should do the trick:

    a =3D ["a", "b", "a", "b", "b"]

    a.uniq.each do |elem|
    puts "#{elem}\t#{a.count(elem)}"
    end


    Produces:

    a 2
    b 3

    Cheers,

    --=20
    JJ Fleck
    PCSI1 Lyc=E9e Kl=E9ber
     
    Fleck Jean-Julien, Oct 5, 2009
    #3
  4. Jim Burgess

    Jim Burgess Guest

    >a = ["a", "b", "a", "b", "b"]
    >a.uniq.each do |elem|
    > puts "#{elem}\t#{a.count(elem)}"
    >end


    @Fleck Jean-Julien
    Thanks for the quick fix. That works perfectly and is miles better than
    what I wrote.

    @Ryan
    Thanks for the suggestions.

    > Your indentation is wack. Make sure you're not mixing spaces and tabs.
    > 2 spaces per indent. You're mixing 2 & 3 & possibly tabs at 8.

    Sorry about that. I'm writing ruby in a virtual Windows on a Linux host.
    Formatting got a bit corrupted when copying the code. I'll watch out for
    that next time.

    > Your variable names are meaningless. Improve that and you'll have a
    > lot better time debugging when it is 4am and you're tired.

    Sorry. I just did that to make the example easier. In reality they are
    called things like '@had_lessons_all' and '@had_lessons_number'.

    > You're not using (sensible) iterators. I suggest you read that section
    > in the pickaxe book.

    Good tip. I will do that now.

    > You're not using good data structures or algorithms.

    No kidding :)

    > count = Hash.new 0
    >
    > # iterate over each string only once
    > strings.each do |string|
    > # increment the count for each string
    > count[string] += 1
    > end
    >
    > p count


    Thanks for that. I will try to rewrite my method (for the sake of
    learning) in a neater way, using the technique you suggest.

    Cheers

    --
    Posted via http://www.ruby-forum.com/.
     
    Jim Burgess, Oct 5, 2009
    #4
  5. Hi --

    On Mon, 5 Oct 2009, Jim Burgess wrote:

    > Hi,
    >
    > I need to count the number of times an element occurs in an array.


    *** DISCLAIMER ***

    This answer is just for fun, and because sometimes seeing useful
    techniques in strange contexts helps people learn and remember them.

    With that in mind... here's a

    >> strings = %w{a b a a c b b b c c c d }

    => ["a", "b", "a", "a", "c", "b", "b", "b", "c", "c", "c", "d"]
    >> hash = Hash.new {|h,k| [k, strings.count(k)] }

    => {}
    >> hash.values_at(*strings.uniq)

    => [["a", 3], ["b", 4], ["c", 4], ["d", 1]]

    It's not really a good way to count array elements, but it's kind of
    an interesting illustration of some hash semantics.


    David

    --
    David A. Black, Director
    Ruby Power and Light, LLC (http://www.rubypal.com)
    Ruby/Rails training, consulting, mentoring, code review
    Book: The Well-Grounded Rubyist (http://www.manning.com/black2)
     
    David A. Black, Oct 5, 2009
    #5
  6. On Mon, Oct 5, 2009 at 6:14 AM, David A. Black <> wrote:
    > Hi --
    >
    > On Mon, 5 Oct 2009, Jim Burgess wrote:
    >
    >> Hi,
    >>
    >> I need to count the number of times an element occurs in an array.

    >
    > *** DISCLAIMER ***
    >
    > This answer is just for fun, and because sometimes seeing useful
    > techniques in strange contexts helps people learn and remember them.
    >
    > With that in mind... here's a
    >
    >>> strings = %w{a b a a c b b b c c c d }

    >
    > => ["a", "b", "a", "a", "c", "b", "b", "b", "c", "c", "c", "d"]
    >>>
    >>> hash = Hash.new {|h,k| [k, strings.count(k)] }

    >
    > => {}
    >>>
    >>> hash.values_at(*strings.uniq)

    >
    > => [["a", 3], ["b", 4], ["c", 4], ["d", 1]]
    >
    > It's not really a good way to count array elements, but it's kind of
    > an interesting illustration of some hash semantics.


    Or, since we don't use a key more than once one could do:

    strings = %w{a b a a c b b b c c c d }

    not_a_hash = lambda {|k| [k, strings.count(k)] }

    strings.uniq.map {|s| not_a_hash}

    => [["a", 3], ["b", 4], ["c", 4], ["d", 1]]




    --
    Rick DeNatale

    Blog: http://talklikeaduck.denhaven2.com/
    Twitter: http://twitter.com/RickDeNatale
    WWR: http://www.workingwithrails.com/person/9021-rick-denatale
    LinkedIn: http://www.linkedin.com/in/rickdenatale
     
    Rick DeNatale, Oct 5, 2009
    #6
  7. Jim Burgess

    Jim Burgess Guest

    Cheers for the replies, they are certainly very interesting and I will
    work through all of them.
    However, now I'm having weird problem.

    When I run the following on Linux:
    a = ["a", "b", "a", "b", "b"]

    a.uniq.each do |elem|
    puts "#{elem}\t#{a.count(elem)}"
    end

    I get the expected output :
    a 2
    b 3

    But when I run exactly the same code on Windows I get:

    undefined method `count' for ["a", "b", "a", "b", "b"]:Array
    (NoMethodError)

    ruby -v (Linux): ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]
    ruby -v (Windows): ruby 1.8.6 (2009-09-24 patchlevel 111) [i386-mswin32]

    Does anyone have an idea why this could be happening.
    I have tried searching for a solution with Google, but could find
    nothing helpful.


    --
    Posted via http://www.ruby-forum.com/.
     
    Jim Burgess, Oct 5, 2009
    #7
  8. Hi --

    On Mon, 5 Oct 2009, Jim Burgess wrote:

    > Cheers for the replies, they are certainly very interesting and I will
    > work through all of them.
    > However, now I'm having weird problem.
    >
    > When I run the following on Linux:
    > a = ["a", "b", "a", "b", "b"]
    >
    > a.uniq.each do |elem|
    > puts "#{elem}\t#{a.count(elem)}"
    > end
    >
    > I get the expected output :
    > a 2
    > b 3
    >
    > But when I run exactly the same code on Windows I get:
    >
    > undefined method `count' for ["a", "b", "a", "b", "b"]:Array
    > (NoMethodError)
    >
    > ruby -v (Linux): ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]
    > ruby -v (Windows): ruby 1.8.6 (2009-09-24 patchlevel 111) [i386-mswin32]
    >
    > Does anyone have an idea why this could be happening.
    > I have tried searching for a solution with Google, but could find
    > nothing helpful.


    count is a post-1.8.6 method.


    David

    --
    David A. Black, Director
    Ruby Power and Light, LLC (http://www.rubypal.com)
    Ruby/Rails training, consulting, mentoring, code review
    Book: The Well-Grounded Rubyist (http://www.manning.com/black2)
     
    David A. Black, Oct 5, 2009
    #8
  9. Jim Burgess

    botp Guest

    On Mon, Oct 5, 2009 at 5:24 PM, Jim Burgess <> wrote:
    > I need to count the number of times an element occurs in an array.
    >


    just for fun

    >> a = ["a", "b", "a", "b", "b"]

    => ["a", "b", "a", "b", "b"]
    >> a.group_by{|x|x}.map{|x,y| [x,y.size]}

    => [["a", 2], ["b", 3]]

    kind regards -botp
     
    botp, Oct 5, 2009
    #9
  10. On Mon, Oct 5, 2009 at 9:22 PM, Jim Burgess <> wrote:
    > Cheers for the replies, they are certainly very interesting and I will
    > work through all of them.
    > However, now I'm having weird problem.
    >
    > When I run the following on Linux:
    > a = ["a", "b", "a", "b", "b"]
    >
    > a.uniq.each do |elem|
    > puts "#{elem}\t#{a.count(elem)}"
    > end
    >
    > I get the expected output :
    > a 2
    > b 3
    >
    > But when I run exactly the same code on Windows I get:
    >
    > undefined method `count' for ["a", "b", "a", "b", "b"]:Array
    > (NoMethodError)
    >
    > ruby -v (Linux): ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]
    > ruby -v (Windows): ruby 1.8.6 (2009-09-24 patchlevel 111) [i386-mswin32]
    >
    > Does anyone have an idea why this could be happening.
    > I have tried searching for a solution with Google, but could find
    > nothing helpful.
    >
    >
    > --


    This works on 1.8.6

    arr = ["a", "b", "a", "b", "b"]
    p arr.uniq.map{|x| [x,arr.select{|y| y == x}.length]}

    #> [["a", 2], ["b", 3]]


    Harry

    --
    A Look into Japanese Ruby List in English
    http://www.kakueki.com/ruby/list.html
     
    Harry Kakueki, Oct 5, 2009
    #10
  11. On Mon, Oct 5, 2009 at 9:22 PM, Jim Burgess <> wrote:
    > Cheers for the replies, they are certainly very interesting and I will
    > work through all of them.
    > However, now I'm having weird problem.
    >
    > When I run the following on Linux:
    > a = ["a", "b", "a", "b", "b"]
    >
    > a.uniq.each do |elem|
    > puts "#{elem}\t#{a.count(elem)}"
    > end
    >
    > I get the expected output :
    > a 2
    > b 3
    >
    > But when I run exactly the same code on Windows I get:
    >
    > undefined method `count' for ["a", "b", "a", "b", "b"]:Array
    > (NoMethodError)
    >
    > ruby -v (Linux): ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]
    > ruby -v (Windows): ruby 1.8.6 (2009-09-24 patchlevel 111) [i386-mswin32]
    >
    > Does anyone have an idea why this could be happening.
    > I have tried searching for a solution with Google, but could find
    > nothing helpful.
    >
    >
    > --


    The solution from Ryan Davis also works on Ruby 1.8.6
    Check it out.

    Harry
    --
    A Look into Japanese Ruby List in English
    http://www.kakueki.com/ruby/list.html
     
    Harry Kakueki, Oct 5, 2009
    #11
  12. Jim Burgess

    Jim Burgess Guest


    > Take a look at this:
    >
    > # use a hash whose values default to 0 for proper data structure use
    > # use count as the name, since that's what it is storing.
    > count = Hash.new 0
    >
    > # iterate over each string only once
    > strings.each do |string|
    > # increment the count for each string
    > count[string] += 1
    > end
    >
    > p count
    >
    > If you nuke all the comments, the code makes just as much sense (if
    > not more). That's what we try to achieve in ruby whenever we can.


    Hi Ryan,

    Don't know if you'll be reading this, but wanted to say thanks anyway.

    After reading the section you suggest from Pickaxe I came up with this,
    which is not perfect, but definitely an improvement:

    array = ["a", "b", "a", "b", "b"]
    count =[]
    for i in 0 ... array.uniq.length
    temp_count = 0
    array.collect{|elem| temp_count +=1 if elem == array.uniq}
    count << temp_count
    end
    p array.uniq, count

    Then I worked through your code and realized what a concise solution you
    had proposed. I have changed my programme accordingly and it is now
    considerably shorter and easier to read.

    Thanks again and thanks to everyone else who made suggestions too.
    --
    Posted via http://www.ruby-forum.com/.
     
    Jim Burgess, Oct 9, 2009
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. mortb
    Replies:
    5
    Views:
    421
    Brock Allen
    Apr 8, 2005
  2. laredotornado
    Replies:
    6
    Views:
    522
    Teraposa Lunodas
    Nov 24, 2009
  3. Thomas Greenwood
    Replies:
    7
    Views:
    166
    David Jacobs
    May 15, 2011
  4. Peng Yu
    Replies:
    3
    Views:
    293
    Dr.Ruud
    Jun 19, 2010
  5. libsfan01
    Replies:
    7
    Views:
    121
    Dr John Stockton
    Aug 13, 2006
Loading...

Share This Page