2013-10-23 14 views
7

Desidero ridurre questo oggetto a solo un oggetto contenente il nome del prodotto e il prezzo medio. Qual è il modo più veloce per farlo?Mappa Oggetto di riduzione con trattino di sottolineatura

var foo = { group1: [ 
     { 
      name: "one", 
      price: 100 
     }, 
     { 
      name: "two", 
      price: 100 
     }], 
     group2: [ 
     { 
      name: "one", 
      price: 200 
     }, 
     { 
      name: "two", 
      price: 200 
     }], 
     group3: [ 
     { 
      name: "one", 
      price: 300 
     }, 
     { 
      name: "two", 
      price: 300 
     }] 
     } 

conseguente

var foo2 = [{ 
       name: 'one', 
       price: 200 
      },{ 
       name: 'two', 
       price: 200 
      }]; 

Grazie!

+0

Dove non '400' viene? – georg

risposta

0

Modifica: Lasciando perdere per ora, ma mi sono completamente dimenticato di _.flatten, quindi redmallard ha un much better answer.

Se si conosce già i nomi di prodotti e appaiono in ogni gruppo, si potrebbe fare il tutto velocemente in questo modo:

var productAveragePrices = function (groups, names) { 
    return _.map(names, function (name) { 
    var product = { name: name }, productPricesSum = 0; 
    _.each(groups, function (group) { 
     productPricesSum += (_.findWhere(group, product).price); 
    }); 
    product.price = productPricesSum/_.size(groups); 
    return product; 
    }); 
}; 
var foo2 = productAveragePrices = function (foo, ['one', 'two']); 

ho messo questo insieme, che dovrebbe funzionare anche se i gruppi hanno diversi prodotti (ad esempio "uno" del primo, secondo e quarto gruppo e "due" in prima e terza):

var productPriceReducer = function(memo, group) { 
    _.each(group, function(product) { 
    // Grabs the current product from the list we're compiling 
    var memoProduct = _.findWhere(memo, { name: product.name }); 
    if (!memoProduct) { 
     // If the product doesn't exist, creates a holder for it and its prices 
     memoProduct = { 
     name: product.name, 
     prices: [ product.price ] 
     }; 
     memo.push(memoProduct); 
    } else { 
     // Otherwise, it just adds the prices to the existing holder. 
     memoProduct.prices.push(product.price); 
    } 
    }); 
    return memo; 
}; 

// This gets us a list of products with all of their prices across groups 
var productPrices = _.reduce(foo, productPriceReducer, []); 

// Then reducing to the average is pretty simple! 
var productAveragePrices = _.map(productPrices, function (product) { 
    var sumPrices = _.reduce(product.prices, function (memo, price) { 
    return memo + price; 
    }, 0); 
    return { 
    name: product.name, 
    price: sumPrices/product.prices.length 
    }; 
}); 

Si potrebbe ancora fare quanto sopra in una funzione con un contatore e sommando i prezzi, ma in questo modo , hai anche i prezzi nel caso tu voglia, per esempio, prendere lo standard d eviazione o trovare la modalità.

+0

bello! grazie mille – vincewilfork

+0

Il mio piacere. Grazie per la mia prima risposta accettata! – Evan

20

Non a pioggia sulla parata di Evan, ma ecco un'alternativa che è un po 'più breve;)

result = _.chain(original) 
    .flatten() 
    .groupBy(function(value) { return value.name; }) 
    .map(function(value, key) { 
    var sum = _.reduce(value, function(memo, val) { return memo + val.price; }, 0); 
    return {name: key, price: sum/value.length}; 
    }) 
    .value(); 

vederlo in azione: http://plnkr.co/edit/lcmZoLkrlfoV8CGN4Pun?p=preview

+3

Questo è meglio! +1 – Evan

+3

.groupBy (function (value) {return value.name;}) può essere scritto semplicemente come .groupBy ('name'). – user239558

3

Mi piace molto la soluzione di redmallard, ma volevo golf un po.

Underscore non include una funzione sum, ma possiamo scrivere espressioni funzionali piuttosto eleganti aggiungendo un mixin sum. Questa funzione è nota come add nel repository underscore-contrib.

Allora possiamo scrivere:

// Somewhere in the initialization of the program 
_.mixin({ 
    sum : function (arr) { 
    return _.reduce(arr, function (s, x) { return s + x;}, 0); 
    } 
}); 
result = _.chain(original) 
    .flatten() 
    .groupBy('name') // shorthand notation 
    .map(function (value, key) { 
     var sum = _.chain(value).pluck('price').sum().value(); 
     return { name: key, price: sum/value.length}; 
    }) 
    .value(); 

http://plnkr.co/edit/ul3odB7lr8qwgVIDOtM9

Ma allora possiamo anche creare un avg mixin di espandere la nostra toolbelt:

// Somewhere in the initialization of the program 
_.mixin({ 
    sum : function (arr) { 
    return _.reduce(arr, function (s, x) { return s + x;}, 0); 
    }, 
    avg : function (arr) { 
    return _.sum(arr)/arr.length; 
    } 
}); 
result = _.chain(original) 
    .flatten() 
    .groupBy('name') // shorthand notation 
    .map(function (value, key) { 
     return { name: key, price: _.avg(value)}; 
    }) 
    .value();