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

A

Adam Akhtar

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 ;)

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.
 
L

Lex Williams

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 :)
 
A

Adam Akhtar

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
 
D

David A. Black

Hi --

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 ;)

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.

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

--
Rails training from David A. Black and Ruby Power and Light:
Advancing With Rails August 18-21 Edison, NJ *
Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
Advancing with Rails January 19-22 Fort Lauderdale, FL
* Co-taught by D.A. Black and Erik Kastner
See http://www.rubypal.com for details and updates!
 
D

David A. Black

Hi --

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 .

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
 
L

Lex Williams

David said:
Hi --

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
 
A

Adam Akhtar

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.
 
T

Thomas Wieczorek

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]
}
 
L

Lex Williams

Adam said:
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"
 
D

David A. Black

Hi --

David said:
Hi --

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 :

What if's? You mean the unless?


David
 
D

David A. Black

Hi --

David said:
Hi --

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

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
 
D

Daniel Bush

Adam said:
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
 
D

David A. Black

Hi --

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.... :)


David
 
A

Adam Akhtar

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.
 
A

Adam Akhtar

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

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top