44416816851f33860b930d58bccdbb09

I was reading the book "Ruby in Practice", and Chapter 1 shows how much can be done in just forty odd lines of Ruby. Except the online code is missing the data set used in the example, or how to load the database. Here is the first chapter online:
\nhttp://www.manning.com/mcanally/Samplechapter1.pdf

[Note: I could not get Scruffy to work at all. I finally substituted Ruffy to generate the PNG, and that worked fine.]

So I wrote some code to load the database, and back-generated the sales data from the one figure given. Given the specific format (nested hash) I picked to hold the sales data, I wanted to find the total number of records to be loaded, so I could then randomize them as I loaded the database.

# random list of sale ndices
index_list = (1..total_sales).to_a.shuffle

This required summing over the nested sales data.

I have been reading "Refactoring: Ruby Edition", and I now see any pattern that looks like the following as a code smell to be ruthlessly eliminated in favor of the Ruby idiomatic versions:

array = []
.each {
array << item
}

or

sum = 0
.each {
sum += item
}

But I can't think of a way to sum over all the sales data without using just such aform. Is there a way to refactor to a double inject for this nested sum, avoiding explicitly initializing a counter? Feel free to reorganize the input data hash/whatever to make that happen.

"I've got that Ruby functional idiom religion!" :-)

Thanks!

require 'rubygems'
require 'active_record'

class Product < ActiveRecord::Base
  has_many :purchases
end

class Purchase < ActiveRecord::Base
  belongs_to :store
end

class Store < ActiveRecord::Base
end

sales = {
    'Cardstock' => {
      'NYC' => 34, 'L.A.' => 30, 'ATL' => 31, 'HSV' => 25, 'PDX' => 37,
      'DAL' => 26, 'NASH' => 27, 'CHI' => 44
  },

    'Foldes' => {
      'NYC' => 118, 'L.A.' => 78, 'ATL' => 67, 'HSV' => 58, 'PDX' => 59,
      'DAL' => 68, 'NASH' => 73, 'CHI' => 97
  },

    'Paper' => {
      'NYC' => 121, 'L.A.' => 64, 'ATL' => 66, 'HSV' => 67, 'PDX' => 61,
      'DAL' => 69, 'NASH' => 59, 'CHI' => 81
  },

    'Envelopes' => {
      'NYC' => 123, 'L.A.' => 62, 'ATL' => 67, 'HSV' => 64, 'PDX' => 57,
      'DAL' => 61, 'NASH' => 75, 'CHI' => 82
  }
}


#total count of all sales, to get maximm 'id' index

total_sales = 0
sales.each_value {|sales_by_city|
  total_sales += sales_by_city.values.inject(0) {|sum, value| sum + value}
}

puts total_sales

Refactorings

No refactoring yet !

A8d3f35baafdaea851914b17dae9e1fc

Adam

October 25, 2010, October 25, 2010 16:45, permalink

1 rating. Login to rate!
total_sales = sales.values.sum { |value| value.values.sum }
2ae5c08e0acf52951e10006f14a56a83

Vincent Durand

October 25, 2010, October 25, 2010 21:02, permalink

1 rating. Login to rate!

Active Record require with this method. I really like active record sum elegant tricks !

total_sales = sales.values.sum(&:values).sum
8f6f95c4bd64d5f10dfddfdcd03c19d6

Rick DeNatale

October 26, 2010, October 26, 2010 02:31, permalink

1 rating. Login to rate!

Like this?

puts sales.values.inject(0) {|total_sales, product_sales_by_city|
  total_sales + product_sales_by_city.values.inject(0) { |product_sales, city_sales| product_sales + city_sales}
  }
44416816851f33860b930d58bccdbb09

A. Lester Buck III

October 27, 2010, October 27, 2010 00:35, permalink

No rating. Login to rate!

Hey guys, thanks for the refactorings! I was looking for Rick's form, but I really like the ActiveRecord forms, too.

Your refactoring





Format Copy from initial code

or Cancel