带有谓词的对象数组的惯用和函数JS约简



从表示股票、其现货价格和其他属性(如)的对象数组

stocks = [ { ticker: 'GOOG', price: 206, ... },
           { ticker: 'AAPL', price: 47, ... },
           { ticker: 'MSFT', price: 39, ... },
           { ticker: 'GOOG', price: 159, ... },
           { ticker: 'MSFT', price: 39, ... },
           { ticker: 'MSFT', price: 21, ... },
           { ticker: 'GOOG', price: 80, ... },
           { ticker: 'AAPL', price: 20, ... },
           { ticker: 'AAPL', price: 73, ... },
           { ticker: 'MSFT', price: 49, ... },
           ... ];

我想以函数的方式返回一个缩减数组,其中对于每个ticker,对象具有最大价格(以及其他属性)。

以下是我目前所拥有的:

c.reduce(function(acc, y) { 
  return acc
    .filter(function(x) { return x.ticker!=y.ticker })
    .concat(
      acc.filter(function(x) { return x.ticker==y.ticker })
        .concat([y])
        .reduce(function(u,v) { 
          return u.price > v.price ? u : v } ) /* predicate */
    )  
}, []);

但是有没有一种更惯用的JavaScript方法呢?欢迎Undercore和lodash,但请不要jQuery。

使用lodash:

var res = _(stocks)
    // group all elements by 'ticker'
    .groupBy(function (s) {
        return s.ticker;
    // sort each group by price descending and get the first element (the max)
    }).map(function (n) {
        return _(n).sortBy(n, function (v) {
            return -v.price;
        }).first();
    });
console.log(JSON.stringify(res, null, 4));

该代码给出:

[
    {
        "ticker": "GOOG",
        "price": 206
    },
    {
        "ticker": "AAPL",
        "price": 73
    },
    {
        "ticker": "MSFT",
        "price": 49
    }
]

较短版本排序ASC并获取最后一个元素:

var res = _(stocks)
    .groupBy('ticker')
    .map(function (n) {
        return _(n).sortBy('price').last();
    });

我尝试用函数概念来解决这个任务。

var stocks =
  [
   {ticker: 'GOOG', price: 206},
   {ticker: 'AAPL', price: 47},
   {ticker: 'MSFT', price: 39},
   {ticker: 'GOOG', price: 159},
   {ticker: 'MSFT', price: 39},
   {ticker: 'MSFT', price: 21},
   {ticker: 'GOOG', price: 80},
   {ticker: 'AAPL', price: 20},
   {ticker: 'AAPL', price: 73},
   {ticker: 'MSFT', price: 49}
 ];
var result = stocks.reduce(function (acc, next) {
  return acc
        .filter(function (stock) {
           return stock.ticker != next.ticker
        })
        .concat(acc.reduce(function (u, v) {
           return u.ticker == v.ticker && u.price < v.price ? v : u;
        }, next));
}, []);

console.log(result);

输出

[ 
 { ticker: 'GOOG', price: 206 },
 { ticker: 'AAPL', price: 73 },
 { ticker: 'MSFT', price: 49 } 
]

这里是一个纯JavaScript解决方案,没有任何只使用一个filter()和一个reduce():的框架

代码

var stocks = [ { ticker: 'GOOG', price: 206},
           { ticker: 'AAPL', price: 47},
           { ticker: 'MSFT', price: 39},
           { ticker: 'GOOG', price: 159},
           { ticker: 'MSFT', price: 39},
           { ticker: 'MSFT', price: 21},
           { ticker: 'GOOG', price: 80},
           { ticker: 'AAPL', price: 20},
           { ticker: 'AAPL', price: 73},
           { ticker: 'MSFT', price: 49}];
function max(arr) {
  return arr.filter(function(item){
    return this[item.ticker] === item;
  }.bind(arr.reduce(function(res, cur) {
    return (res[cur.ticker] = (res[cur.ticker] 
        ? (res[cur.ticker].price > cur.price ? res[cur.ticker] : cur)
        : cur
    )), res;
  }, {})));
}
console.log(max(stocks));

结果

[ 
 { ticker: 'GOOG', price: 206 },
 { ticker: 'AAPL', price: 73 },
 { ticker: 'MSFT', price: 49 } 
]

JSBin

https://jsbin.com/linevudika/edit?js,控制台

最新更新