Array of hashes - need to iterate and calulate stats but how

I making a script which generates some basic stats for completed
listings on ebay. Its merely a means to improve on my ruby so its pretty
primitive :wink:

I have an array of hashes. Each hash represents a listing on ebay.
e.g.
{:title => “Canon Accessory R45-WD45”, :model_number =>“R45-WD45”
:did_it_sell => TRUE}

Within in the array there will be many listings for the same product.
E.g. 25 wiis, 45 xboxes 35 sigma 18-50mm lenses etc.

I want to work out statistics for each product grouping. Teh desired
output would be something like this

Wii - Total listings 25. Total sold 10. Sell through rate 40%
Xbox - Total listings 50. Total sold 2. Sell through rate 4%

So i need to at least sort the array of products in order of their model
number passing sort a custom block.

From here on im not sure how to proceed. Should I bundle the groupings
of model numbers into their own arrays or should i simply iterate over
this nicely sorted array and make the script keep a mental note of what
product its calculating for.

in C i would have used a structure to contain the product information.
Is a hash the best thing to use?

Im not looking for someone to write the code but rather a general
outline of how this type of stuff is usually done. any help greatly
apprecated.

something like this :

statistic_hash = {}

array.each do |hash|
if(hash[:did_it_sell)
if(statistic_hash.has_key? hash[:title]
statistic_hash[hash[:title]] += 1
else
statistic_hash[hash[:title]] = 0
end
end
end

and then you would have a statistic_hash containing as key a product
name , and as value , the number of units sold. I’m not sure about how
to calculate the statistics .

I’m a ruby newbie , so I hope this was helpful :slight_smile:

Thanks for the response,

your solution would be great but the problem is i cant use a hash to
store the output as I need to store more than two values. I need to have
model number, total listed, total sold and finaly the sell through rate
based on the previous two numbers.

Ive tried to rejig teh general jist of your approach but couldnt.

Heres my very rough solution. Surely theres a more elegant way to this

number_listed = 0
number_sold = 0
current_model = item_info[0][:unique_identifier]
item_info.each do |item|
if current_model != item[:unique_identifier]
result = {unique_identifier => current_model, :total_listings
=> number_listed, :total_sold => number_sold, :str =>
number_sold/number_listed*100.00}
number_sold = 0
numbler_listed = 0
unique_identifier = item[:unique_identifier]
end
number_sold += if item[:did_it_sell]
number_listed +=
end

unique_identifier in the above code is merely refers to the model
number.

Hi –

On Wed, 20 Aug 2008, Adam A. wrote:

E.g. 25 wiis, 45 xboxes 35 sigma 18-50mm lenses etc.

From here on im not sure how to proceed. Should I bundle the groupings
of model numbers into their own arrays or should i simply iterate over
this nicely sorted array and make the script keep a mental note of what
product its calculating for.

in C i would have used a structure to contain the product information.
Is a hash the best thing to use?

You could probably generate a report fairly easily in this case with
hash and array manipulation, but in general, in cases where you’d be
likely to create a structure in C, in Ruby you’d typically write a
class that has the attributes and methods you need. So maybe something
like this:

class Item
attr_reader :title, :model_number, :sold

def initialize(hash)
@title, @model_number, @sold = hash.values_at(:title,
:model_number, :did_it_sell)
end

def <=>(other)
model_number <=> other.model_number
end
end

and then if you have an array of hashes you can turn it into an array
of items:

items = hashes.map {|h| Item.new(h) }

and then items.sort will be sorted by model number (because of the <=>
definition in Item).

David

Hi –

On Wed, 20 Aug 2008, Lex W. wrote:

   end

end
end

and then you would have a statistic_hash containing as key a product
name , and as value , the number of units sold. I’m not sure about how
to calculate the statistics .

You can use a hash with a default value to make all that hash testing
easier:

statistic_hash = Hash.new(0)
array.each do |hash|
next unless hash[:did_it_sell]
statistic_hash[hash[:title]] += 1
end

David

hi guys thanks for the advice. id like to stick with processing a hash
rather htan a class for the moment.

The problme is the output object cant be a hash, it would have to be an
array of hashes as in my reply to lex ill want to return more than two
values for each item.

ill need the model number, the total nubmer of listings for that model,
the number of which sold and from those the sell through rate
(percentage that sold).

With a hash i can only have a key and a value which is a shame as those
solutions above looked good. I cant think of a way to rejig them to work
with an array of hashes.

or is my assumption wrong.

David A. Black wrote:

Hi –

On Wed, 20 Aug 2008, Lex W. wrote:

   end

end
end

and then you would have a statistic_hash containing as key a product
name , and as value , the number of units sold. I’m not sure about how
to calculate the statistics .

You can use a hash with a default value to make all that hash testing
easier:

statistic_hash = Hash.new(0)
array.each do |hash|
next unless hash[:did_it_sell]
statistic_hash[hash[:title]] += 1
end

David

you could use select on the array , and lose those if’s . something like
this :

array.select { |hash| hash[:did_it_sell] }.each do |hash|
statistic_hash[hash[:title]] += 1
end

adam , how about an array ref as the value for a certain key?

hi lex,

im not so hot on ruby, could you expand on what you mean by that?

You can also create Structs in Ruby if you prefer them

Item = Struct.new(:title, :article_number)
it = Item.new(“Wii”, “Wii”)
it.title #=>“Wii”

You can add as many members as you like in the Struct constructor.

As to go with your statistics, I agree with the others. I’d use a
statitiscs Hash and count the different items:

stats_hash = { }
Stats = Struct.new(:count, :sold)
item_array.each { |it|
unless hash[item[:article_number]]
hash[item[:article_number]] = Stats.new(0, 0)
end
hash[item[:article_number]].count += 1
hash[item[:article_number]].sold += 1 if item[:did_it_sell]
}

Hi –

On Wed, 20 Aug 2008, Lex W. wrote:

name , and as value , the number of units sold. I’m not sure about how

David

you could use select on the array , and lose those if’s . something like
this :

What if’s? You mean the unless?

David

David A. Black wrote:

Hi –

On Wed, 20 Aug 2008, Lex W. wrote:

name , and as value , the number of units sold. I’m not sure about how

David

you could use select on the array , and lose those if’s . something like
this :

What if’s? You mean the unless?

David

I was referring to the first snippet I posted.

Adam A. wrote:

hi lex,

im not so hot on ruby, could you expand on what you mean by that?

well , for a hash , you can have an object as the key , and an object as
the value. This means that there’s nothing stopping us to have an array
as the value for a key.

Example :

h = {
“a”=>[“b”,“c”,“d”]
}

puts h[“a”] # this will print b,c,d . we have an array associated to the
key “a”

Hi –

On Wed, 20 Aug 2008, Lex W. wrote:

you could use select on the array , and lose those if’s . something like
this :

What if’s? You mean the unless?

David

I was referring to the first snippet I posted.

Whoops, I forgot you had posted the one I had responded to in the
first place. /me reaches for the coffee… :slight_smile:

David

Hi –

On Wed, 20 Aug 2008, Lex W. wrote:

name , and as value , the number of units sold. I’m not sure about how

David

you could use select on the array , and lose those if’s . something like
this :

array.select { |hash| hash[:did_it_sell] }.each do |hash|
statistic_hash[hash[:title]] += 1
end

To answer my own question: I guess you do mean the ‘unless’. I
deliberately didn’t use select just to save the creation of an
intermediate object, and because I think that the ‘next unless’ line
is nice and expressive.

David

Adam A. wrote:

hi lex,

im not so hot on ruby, could you expand on what you mean by that?

Hi Adam,
I quite like the class and struct approaches already mentioned but I had
a stab at just using plain arrays and hashes (not extensively tested):

require ‘pp’
items = [
{ :title => ‘title1’ , :model => ‘123a’ , :sold => false },
{ :title => ‘title2’ , :model => ‘123a’ , :sold => false },
{ :title => ‘title3’ , :model => ‘123b’ , :sold => true },
{ :title => ‘title4’ , :model => ‘123c’ , :sold => false },
{ :title => ‘title5’ , :model => ‘123c’ , :sold => true }
]

summary=Hash.new({:listings => 0 , :sold => 0 , :rate => 0})
items.each do |item|

Store default value if key in ‘summary’ doesn’t exist.

summary[item[:model]]=summary.default.clone unless
summary.has_key?(item[:model])
model=summary[item[:model]]

model[:listings]+=1
model[:sold]+=1 if item[:sold]
model[:rate]=model[:sold].to_f/model[:listings].to_f

end

pp summary

Regards,
Daniel

and aplogies for my dreadful typing and spelling, im actually british
but you probably couldnt tell that from the typos above.

Thanks everyone for your help. Its a small script and im apart from the
above im not using the data for anything else so far now ill proceed
with hashes and arrays though ill use the idea of adding to a new hash
and checking if theres already a key in there before creating a new one.

However i do like to challenge myself and classes and structs in ruby
are still a little foreign so ill spend some time after completing this
little project to switch to using classes.

Thanks again.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs