Skip to content

Instantly share code, notes, and snippets.

@evanlh
Created September 14, 2015 02:55
Show Gist options
  • Save evanlh/31179a49a753b0292db2 to your computer and use it in GitHub Desktop.
Save evanlh/31179a49a753b0292db2 to your computer and use it in GitHub Desktop.
/**
* Creates a new Cart object, with an optional pricing parameter.
* @class
* @param {object} pricing
*/
function Cart(prices){
this.total = 0,
this.pricing = {},
this.contents = {};
if (prices){
this.setPricing(prices);
}
};
Cart.prototype = {
/**
* Assign pricing details to a Cart object.
* @method
* @param {object} pricing - A map from item_id to a map of
* {quantity1: price1, quantity2: price2}
* @returns {object} cart instance
*/
setPricing: function (prices){
var self = this;
for (var item in prices){
if (!prices.hasOwnProperty(item)) continue;
// allow shortcut for qty == 1 items
if (typeof prices[item] == 'number'){
this.pricing[item] = { 1: prices[item] };
}
// copy qty => price pairs
else if (typeof prices[item] == 'object'){
var qtyKeys = Object.keys(prices[item]);
this.pricing[item] = {};
qtyKeys.forEach(function(key){
self.pricing[item][key] = prices[item][key];
});
}
else {
throw new Error('Unsupported price format');
}
}
return this;
},
/**
* Scan the specified item_id quantity times
* @param {string} item_id - Unique id of the item to scan
* @param {integer} quantity - (Optional) number of items, default 1.
* @returns {object} cart instance
*/
scan: function(item_id, quantity){
if (!quantity) quantity = 1;
if (!item_id || typeof this.pricing[item_id] !== 'object') {
throw new Error('No pricing data available for ' + item_id +
'. Maybe you forgot to setPricing()?');
}
if (this.contents[item_id]){
this.contents[item_id] += quantity;
}
else {
this.contents[item_id] = quantity;
}
return this;
},
/**
* Calculate the cart total, applying quantity discounts.
* Retrieve the result from the .total property.
* @returns {object} cart instance
*/
calculateTotal: function(){
var cumulative = 0;
for (var item in this.contents){
cumulative += this.calculateItemTotal(item, this.contents[item]);
}
this.total = cumulative;
return this;
},
/**
* Calculate the line-item total for specified item_id & quantity
* @returns {number} item total
*/
calculateItemTotal: function(item_id, qty){
var itemPricing = this.pricing[item_id],
qtyKeys = Object.keys(itemPricing).sort().reverse(),
curQty = qty,
cumulative = 0;
for (var i = 0; i < qtyKeys.length;){
var blockQty = qtyKeys[i];
if (blockQty > curQty) {
i++;
continue;
}
cumulative += itemPricing[blockQty];
curQty -= blockQty;
if (curQty < blockQty) i++;
}
return cumulative;
}
};
function test(a, b){
if (a === b){
console.log("Test PASSED: " + a + " === " + b);
}
else {
console.log("Test FAILED: " + b + " !== " + b);
}
}
var testPricing = {
A: { 1: 2, 4: 7},
B: 12,
C: { 1: 1.25, 6: 6 },
D: 0.15
};
var cartA = new Cart(testPricing);
cartA.scan('A')
.scan('B')
.scan('C')
.scan('D')
.scan('A')
.scan('B')
.scan('A', 2)
.calculateTotal();
test(cartA.total, 32.40);
var cartB = new Cart();
cartB.setPricing(testPricing)
.scan('C', 7)
.calculateTotal();
test(cartB.total, 7.25);
var cartC = new Cart(testPricing);
cartC.scan('A')
.scan('B')
.scan('C')
.scan('D')
.calculateTotal();
test(cartC.total, 15.40);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment