Inject problem

Hello,

I have a array like this [ 1.2.1.1]
Now I want a hash which contains the numbers and the count.
So I thought this would work : count.inject { |1, array.count(1)| }

But I get a lot of syntax errors.

Can anyone give me a hint where I am going wrong ?

Roelof

Hello,

I have a array like this [ 1.2.1.1]

Well, first of all this is not correct. It should be:

count = [1,2,1,1]

Now I want a hash which contains the numbers and the count.
So I thought this would work : count.inject { |1, array.count(1)| }

This doesn't even make sense to me.

But I get a lot of syntax errors.

Can anyone give me a hint where I am going wrong ?

If you want to do that you need to create a Hash. Then iterate through
the array adding 1 to the value in the hash for each element of the
array.

Jesus.

···

On Tue, Sep 25, 2012 at 10:41 AM, Roelof Wobben <rwobben@hotmail.com> wrote:

Well the simplest error is the count.inject{| ... |} part. The stuff
that goes between the | ... | are variable declarations. For example
this is sum for an array

array = [1,2,1,1]
array.inject(0){|x, y| x + y} => 5

note the variable 'x' and 'y' are between the | ... | and the action
'x + y' takes place after. inject is expecting two variable to be
declared so that is another error.

Hi,

Using "inject" really isn't a good choice here. If at all, I'd use
"each_with_object" to build up the hash.

If you don't need the whole hash at once, you could also use the block
form of Hash.new to calculate the elements on demand:

arr = [1, 2, 1, 1]

count_hash = Hash.new do |hash, key|
  cnt = arr.count key
  cnt == 0 ? nil : hash[key] = cnt
end

p count_hash[1]

Or you could do something like this:

arr = [1, 2, 1, 1]

arr_uniq = arr.uniq
count_hash = Hash[arr_uniq.zip arr_uniq.map {|e| arr.count e}]

p count_hash

···

--
Posted via http://www.ruby-forum.com/.

The first parameter of the "inject" block is always the intermediate
result used for the aggregation. And the second is the current element.

For example:

# calculate sum of 1, 2, ..., 10 (in an inefficient way)
sum = (1..10).inject do |intermediate_sum, integer|
  intermediate_sum + integer
end

Or in your case:

count = [1, 2, 1, 1]
hash = count.inject({}) do |intermediate_hash, number|
  intermediate_hash[number] += 1 rescue intermediate_hash[number] = 1
  intermediate_hash
end
puts hash

···

--
Posted via http://www.ruby-forum.com/.

You're iterating over the wrong object. After you've build the hash, you
have to iterate over this hash and not the dices arrays again.

The dices array contains only the numbers. The h hash contains the
numbers and corresponding counts.

So it's not

dice.each {|number, count| ...

but

h.each {|number, count| ...

Since there have been quite some errors now, you should be more careful
with the code you write. And you should get used to testing and
debugging code yourself. The error message is telling you that the ">"
operator has been applied to nil (namely the "count" variable). So if
you didn't already know the problem, the first step would be to check
why "count" is nil. You'd do this by outputting "dice", since that's
what you're iterating over. And then you'd probably realized that it's
the completely wrong object.

···

--
Posted via http://www.ruby-forum.com/.

try this,
count.inject({}){|x,y| x[y] += 1 rescue x[y] = 1; x}

···

On Tue, Sep 25, 2012 at 2:25 PM, Peter Hickman < peterhickman386@googlemail.com> wrote:

Well the simplest error is the count.inject{| ... |} part. The stuff
that goes between the | ... | are variable declarations. For example
this is sum for an array

array = [1,2,1,1]
array.inject(0){|x, y| x + y} => 5

note the variable 'x' and 'y' are between the | ... | and the action
'x + y' takes place after. inject is expecting two variable to be
declared so that is another error.

My preferred solution:

count = [1,2,1,1]
h = Hash.new(0)
count.each {|el| h[el] += 1}
p h #=> {1=>3, 2=>1}

Jesus.

···

On Tue, Sep 25, 2012 at 11:31 AM, Jan E. <lists@ruby-forum.com> wrote:

Hi,

Using "inject" really isn't a good choice here. If at all, I'd use
"each_with_object" to build up the hash.

Hello,

I thought I solved this problem with this code :

def score(dice)
  total = 0
  h = Hash.new(0)
  dice.each {|el| dice[el] += 1}

  dice.each {|number, count|
   if number == 1 and count == 3 then total = total + 1000 end
   if number == 1 and count == 6 then return 2000 end
   if number != 1 and count == 3 then total = total + 100 * count end
   if number != 1 and count == 6 then return 600 end
   if number == 1 and (count > 3) then total = total + ( 100 + ( count - 3)) end
   if number == 1 and (count < 3) then total = total + 100 * count end
   if number == 5 and (count > 3) then total = total + ( 50 * (count -3)) end
   if number == 5 and (count < 3) then total = total + 50 * count end
    }
  return total
end

But now I'm getting this error : undefined method `+' for nil:NilClass

Roelof

···

Date: Tue, 25 Sep 2012 23:31:12 +0900
From: lists@ruby-forum.com
Subject: Re: inject problem
To: ruby-talk@ruby-lang.org

The first parameter of the "inject" block is always the intermediate
result used for the aggregation. And the second is the current element.

For example:

# calculate sum of 1, 2, ..., 10 (in an inefficient way)
sum = (1..10).inject do |intermediate_sum, integer|
  intermediate_sum + integer
end

Or in your case:

count = [1, 2, 1, 1]
hash = count.inject({}) do |intermediate_hash, number|
  intermediate_hash[number] += 1 rescue intermediate_hash[number] = 1
  intermediate_hash
end
puts hash

--
Posted via http://www.ruby-forum.com/\.

Thanks.

I know I have a lot to learn.
I'm just beginning with ruby and in this moments I miss a IDE where I can do everything step by step so I can see what went wrong.

Now refractor the if then 's and I hope I can decline it to less then 5 if then's.
But that will be difficult.

Roelof

···

Date: Wed, 26 Sep 2012 05:51:29 +0900
From: lists@ruby-forum.com
Subject: Re: inject problem
To: ruby-talk@ruby-lang.org

You're iterating over the wrong object. After you've build the hash, you
have to iterate over this hash and not the dices arrays again.

The dices array contains only the numbers. The h hash contains the
numbers and corresponding counts.

So it's not

dice.each {|number, count| ...

but

h.each {|number, count| ...

Since there have been quite some errors now, you should be more careful
with the code you write. And you should get used to testing and
debugging code yourself. The error message is telling you that the ">"
operator has been applied to nil (namely the "count" variable). So if
you didn't already know the problem, the first step would be to check
why "count" is nil. You'd do this by outputting "dice", since that's
what you're iterating over. And then you'd probably realized that it's
the completely wrong object.

--
Posted via http://www.ruby-forum.com/\.

All thanks for the explanation.

I have one question about the solution of Prasadhnc C

When I do this :

array.each do |x|
     count.inject({}){|x,y| x[y] += 1 rescue x[y] = 1; x}
end

where x = array [ 1, 2 , 1 , 1]

Then I don't get the counted answers.

Roelof

···

Date: Tue, 25 Sep 2012 18:48:29 +0900
From: jgabrielygalan@gmail.com
Subject: Re: inject problem
To: ruby-talk@ruby-lang.org

On Tue, Sep 25, 2012 at 11:31 AM, Jan E. <lists@ruby-forum.com> wrote:
> Hi,
>
> Using "inject" really isn't a good choice here. If at all, I'd use
> "each_with_object" to build up the hash.

My preferred solution:

count = [1,2,1,1]
h = Hash.new(0)
count.each {|el| h[el] += 1}
p h #=> {1=>3, 2=>1}

Jesus.

This would imply that dice[el] is nil. Not surprising, since |el| is
the element, not the index. Maybe you meant something like:

  dice.map! { |el| el + 1 }

or

  dice.each_index { |idx| dice[idx] += 1 }

?

-Dave

···

On Tue, Sep 25, 2012 at 1:57 PM, Roelof Wobben <rwobben@hotmail.com> wrote:

But now I'm getting this error : undefined method `+' for nil:NilClass

On what line? Presumably the one that says:

dice.each {|el| dice[el] += 1}

--
Dave Aronson, T. Rex of Codosaurus, LLC... aka
Available Secret-Cleared Ruby/Rails Freelancer
(NoVa/DC/Remote); see http://www.Codosaur.us/\.

Roelof Wobben wrote in post #1077503:

  h = Hash.new(0)
  dice.each {|el| dice[el] += 1}
[...]
But now I'm getting this error : undefined method `+' for nil:NilClass

You want h[el] += 1, not dice[el] += 1. See the original code by Jesús.

···

--
Posted via http://www.ruby-forum.com/\.

Roelof Wobben wrote in post #1077587:

I'm just beginning with ruby and in this moments I miss a IDE where I
can do everything step by step so I can see what went wrong.

Well, I wouldn't rely too much on the IDE. The danger of this is that it
leads to a kind of "trial and error" programming: You just write
something down and then use the debugger to make actual working code out
of it.

That's not how it should be, especially when you've just started. The
code reflects your thoughts, so it should at least be logical in itself.
Sure, we all make mistakes, so there will always be typos, syntax errors
etc. But when your code is logical incorrect, it shows that you've not
really thought this through (or maybe didn't even understand the parts
you've copied and pasted).

This is no critique or something, just a suggestion. In school exams, we
actually have to write down our code on a piece of paper. This way we
focus on clear thoughts, not so much on getting the code running
somehow.

Now refractor the if then 's and I hope I can decline it to less then 5
if then's.

I don't understand the logic, so I can't help you with that. But you
should structure the cases with nested "if"s.

···

--
Posted via http://www.ruby-forum.com/\.

I think you should not structure the code this way. I think you should
loop through the counts, finding and removing sets. Something like
(pseudocode):

while there are sets of 3 ones:
  remove the set from the hash (h[1] -= 3)
  sum 1000 to the score
end

while there are sets of 3 any other pip count:
  remove the set from the hash (h -= 3)
  sum 100*x to the score
end

sum 100 * (remaining number of ones)
sum 50 * (remaining number of fives)

This way, your code is more simple and robust. For example this will
also work if you throw 9 dice, while your current algorithm does not.

Jesus.

···

On Wed, Sep 26, 2012 at 9:39 AM, Roelof Wobben <rwobben@hotmail.com> wrote:

Now refractor the if then 's and I hope I can decline it to less then 5 if
then's.
But that will be difficult.

just do:

array = [ 1, 2 , 1 , 1]

p array.inject({}){|x,y| x[y] += 1 rescue x[y] = 1; x}

Roelof Wobben wrote in post #1077457:

I have one question about the solution of Prasadhnc C

Haven't you read the other postings? :frowning:

"inject" is completely useless in your case, since you don't do what
it's meant for: building a value by subsequently calling the block. You
just pass the same object again and again. Yes, "inject" still works.
But you basically circumvent the actual mechanism -- in which case
"each_with_object" would be a better fit.

Or do you want to use "inject" no matter what?

···

--
Posted via http://www.ruby-forum.com/\.

Thanks but I did not solve my problem.

Here is the whole script :

require File.expand_path(File.dirname(__FILE__) + '/edgecase')

# Greed is a dice game where you roll up to five dice to accumulate
# points. The following "score" function will be used to calculate the
# score of a single roll of the dice.

···

#
# A greed roll is scored as follows:
#
# * A set of three ones is 1000 points
#
# * A set of three numbers (other than ones) is worth 100 times the
# number. (e.g. three fives is 500 points).
#
# * A one (that is not part of a set of three) is worth 100 points.
#
# * A five (that is not part of a set of three) is worth 50 points.
#
# * Everything else is worth 0 points.
#
#
# Examples:
#
# score([1,1,1,5,1]) => 1150 points
# score([2,3,4,6,2]) => 0 points
# score([3,4,5,3,3]) => 350 points
# score([1,5,1,2,4]) => 250 points
#
# More scoring examples are given in the tests below:
#
# Your goal is to write the score method.

def score(dice)
  total = 0
  h = Hash.new(0)
  dice.each {|el| h[el] += 1}

dice.each {|number, count|
   if number == 1 and count == 6 then return 2000 end
   if number != 1 and count == 3 then total = total + 100 * count end
   if number != 1 and count == 6 then return 600 end
   if number == 1 and (count > 3) then total = total + ( 100 + ( count - 3)) end
   if number == 1 and (count < 3) then total = total + 100 * count end
   if number == 5 and (count > 3) then total = total + ( 50 * (count -3)) end
   if number == 5 and (count < 3) then total = total + 50 * count end
    }

end
  
class AboutScoringProject < EdgeCase::Koan
  def test_score_of_an_empty_list_is_zero
    assert_equal 0, score()
  end

  def test_score_of_a_single_roll_of_5_is_50
    assert_equal 50, score([5])
  end

  def test_score_of_a_single_roll_of_1_is_100
    assert_equal 100, score([1])
  end

  def test_score_of_multiple_1s_and_5s_is_the_sum_of_individual_scores
    assert_equal 300, score([1,5,5,1])
  end

  def test_score_of_single_2s_3s_4s_and_6s_are_zero
    assert_equal 0, score([2,3,4,6])
  end

  def test_score_of_a_triple_1_is_1000
    assert_equal 1000, score([1,1,1])
  end

  def test_score_of_other_triples_is_100x
    assert_equal 200, score([2,2,2])
    assert_equal 300, score([3,3,3])
    assert_equal 400, score([4,4,4])
    assert_equal 500, score([5,5,5])
    assert_equal 600, score([6,6,6])
  end

  def test_score_of_mixed_is_sum
    assert_equal 250, score([2,5,2,2,3])
    assert_equal 550, score([5,5,5,5])
  end

end

And I now get this error: <0> expected but was <>.
When I make that one a comment :

def test_score_of_an_empty_list_is_zero

    #assert_equal 0, score()

  end

Then I see this error appear :

The answers you seek...
  undefined method `>' for nil:NilClass

Please meditate on the following code:
  ./about_scoring_project.rb:43:in `score'
  ./about_scoring_project.rb:37:in `each'
  ./about_scoring_project.rb:37:in `score'
  ./about_scoring_project.rb:56:in `test_score_of_a_single_roll_of_5_is_50'
  /home/roelof/koans/edgecase.rb:398:in `send'
  /home/roelof/koans/edgecase.rb:398:in `meditate'
  /home/roelof/koans/edgecase.rb:470:in `walk'
  /home/roelof/koans/edgecase.rb:481:in `each_step'
  /home/roelof/koans/edgecase.rb:479:in `each'
  /home/roelof/koans/edgecase.rb:479:in `each_step'
  path_to_enlightenment.rb:38:in `each_with_index'
  /home/roelof/koans/edgecase.rb:478:in `each'
  /home/roelof/koans/edgecase.rb:478:in `each_with_index'
  /home/roelof/koans/edgecase.rb:478:in `each_step'
  /home/roelof/koans/edgecase.rb:476:in `catch'
  /home/roelof/koans/edgecase.rb:476:in `each_step'
  /home/roelof/koans/edgecase.rb:469:in `walk'
  /home/roelof/koans/edgecase.rb:491
  path_to_enlightenment.rb:38

So something is not well here ?

Roelof

Date: Wed, 26 Sep 2012 03:26:55 +0900
From: lists@ruby-forum.com
Subject: Re: inject problem
To: ruby-talk@ruby-lang.org

Roelof Wobben wrote in post #1077503:
> h = Hash.new(0)
> dice.each {|el| dice[el] += 1}
> [...]
> But now I'm getting this error : undefined method `+' for nil:NilClass

You want h[el] += 1, not dice[el] += 1. See the original code by Jesús.

--
Posted via http://www.ruby-forum.com/\.

No problem.

I try to learn ruby by following ruby koans.
But I think now it's not a good idea.

They work with test-driven development.
So every test on koans fail but most of the times in the error message is hidden what the right answer is.
In the end you have to use that knowlegde to solve puzzles like this.

So i'm looking for a way that works for me.

Roelof

···

Date: Wed, 26 Sep 2012 18:35:24 +0900
From: lists@ruby-forum.com
Subject: Re: inject problem
To: ruby-talk@ruby-lang.org

Roelof Wobben wrote in post #1077587:
> I'm just beginning with ruby and in this moments I miss a IDE where I
> can do everything step by step so I can see what went wrong.

Well, I wouldn't rely too much on the IDE. The danger of this is that it
leads to a kind of "trial and error" programming: You just write
something down and then use the debugger to make actual working code out
of it.

That's not how it should be, especially when you've just started. The
code reflects your thoughts, so it should at least be logical in itself.
Sure, we all make mistakes, so there will always be typos, syntax errors
etc. But when your code is logical incorrect, it shows that you've not
really thought this through (or maybe didn't even understand the parts
you've copied and pasted).

This is no critique or something, just a suggestion. In school exams, we
actually have to write down our code on a piece of paper. This way we
focus on clear thoughts, not so much on getting the code running
somehow.

> Now refractor the if then 's and I hope I can decline it to less then 5
> if then's.

I don't understand the logic, so I can't help you with that. But you
should structure the cases with nested "if"s.

--
Posted via http://www.ruby-forum.com/\.

Interresting idea.

I will try if I can make it work this afternoon or this evening.

Roelof

···

Date: Wed, 26 Sep 2012 18:44:24 +0900
From: jgabrielygalan@gmail.com
Subject: Re: inject problem
To: ruby-talk@ruby-lang.org

On Wed, Sep 26, 2012 at 9:39 AM, Roelof Wobben <rwobben@hotmail.com> wrote:
> Now refractor the if then 's and I hope I can decline it to less then 5 if
> then's.
> But that will be difficult.

I think you should not structure the code this way. I think you should
loop through the counts, finding and removing sets. Something like
(pseudocode):

while there are sets of 3 ones:
  remove the set from the hash (h[1] -= 3)
  sum 1000 to the score
end

while there are sets of 3 any other pip count:
  remove the set from the hash (h -= 3)
  sum 100*x to the score
end

sum 100 * (remaining number of ones)
sum 50 * (remaining number of fives)

This way, your code is more simple and robust. For example this will
also work if you throw 9 dice, while your current algorithm does not.

Jesus.