【问题标题】:How to pick latest item per user from an array?如何从数组中为每个用户选择最新的项目?
【发布时间】:2016-12-24 04:14:20
【问题描述】:

例如,我有一个这样的数组:

[ 
{  
  "data": "some data",
  "user": "A",
  "createdAt":"2016-12-24",
},
{
  "data": "some data2",
  "user": "B",
  "createdAt":"2016-12-24",
},
{
  "data": "some data3",
  "user": "A",
  "createdAt":"2016-12-25",
},
{
  "data": "some data4",
  "user": "A",
  "createdAt":"2016-12-23",
},
{
  "data": "some data5",
  "user": "B",
  "createdAt":"2016-12-25",
},
{
  "data": "some data6",
  "user": "C",
  "createdAt":"2016-12-24",
},
] 

我只需要每个用户最新创建的项目,所以我需要一个这样的数组:

[ 
{
  "data": "some data3",
  "user": "A",
  "createdAt":"2016-12-25",
},
{
  "data": "some data5",
  "user": "B",
  "createdAt":"2016-12-25",
},
{
  "data": "some data6",
  "user": "C",
  "createdAt":"2016-12-24",
},
] 

如何使用下划线或 lodash 来实现?

【问题讨论】:

    标签: javascript underscore.js lodash


    【解决方案1】:

    lodash 提供了orderByuniqBy 方法。

    关键是按user 排序,然后按createdAt 降序排列。由于createdAtDate,因此需要对其进行处理。方便的是,orderBy 方法允许指定要排序的属性或要评估的函数(返回要排序的对象,在本例中为 Date)。

    var data = [{
          "data": "some data",
          "user": "A",
          "createdAt": "2016-12-24",
        }, {
          "data": "some data2",
          "user": "B",
          "createdAt": "2016-12-24",
        }, {
          "data": "some data3",
          "user": "A",
          "createdAt": "2016-12-25",
        }, {
          "data": "some data4",
          "user": "A",
          "createdAt": "2016-12-23",
        }, {
          "data": "some data5",
          "user": "B",
          "createdAt": "2016-12-25",
        }, {
          "data": "some data6",
          "user": "C",
          "createdAt": "2016-12-24",
        }],
        sorted = _.orderBy(
          data,
          ['user', function(d) {
            return new Date(d.createdAt)
          }],
          ['asc', 'desc'] // 'desc' orders by date descending
        ),
        uniqued = _.uniqBy(sorted, 'user');
    
    console.log(uniqued);

    【讨论】:

      【解决方案2】:

      另一种纯 Javascript 解决方案,带有哈希表并检查以后的createdAt

      var data = [{ "data": "some data", "user": "A", "createdAt": "2016-12-24", }, { "data": "some data2", "user": "B", "createdAt": "2016-12-24", }, { "data": "some data3", "user": "A", "createdAt": "2016-12-25", }, { "data": "some data4", "user": "A", "createdAt": "2016-12-23", }, { "data": "some data5", "user": "B", "createdAt": "2016-12-25", }, { "data": "some data6", "user": "C", "createdAt": "2016-12-24", }],
          result = data.reduce(function (hash) {
              return function (r, a) {
                  if (!(a.user in hash)) {
                      hash[a.user] = r.push(a) - 1;
                      return r;
                  }
                  if (r[hash[a.user]].createdAt < a.createdAt) {
                      r[hash[a.user]] = a;
                  }
                  return r;
              };
          }(Object.create(null)), []);
      
      console.log(result);
      .as-console-wrapper { max-height: 100% !important; top: 0; }

      【讨论】:

      • 我第一次遇到我不完全理解的代码,它可以工作并且是很好的干净代码。据我所见,脚本级别没有真正的循环——这有点像 LINQ 的等价物,但在 JavaScript 中?所有数据都通过reduce 推送到方法,并且方法递归执行?
      • @SamuelJackson,基本上它使用hash 上的闭包和一个回调,它被data 的每个元素调用,这在回调中由a 表示。结果收集在r 中并为每个循环返回。虽然每个项目都会调用回调,但它不是递归调用,因为它不会调用自身。
      • 非常性感的 javascript。感谢您的信息。通过递归,我的意思是将数据传递给数组中每个项目的该方法,并返回结果。在这种方式下,它类似于.NET 的 LINQ。我喜欢它
      【解决方案3】:

      使用普通的javascript,你可以这样做

      var myArray = [ 
          {  
                "data": "some data",
                "user": "A",
                "createdAt":"2016-12-24",
          },
          {
                "data": "some data2",
                "user": "B",
                "createdAt":"2016-12-24",
          },
          {
                "data": "some data3",
                "user": "A",
                "createdAt":"2016-12-25",
          },
          {
                "data": "some data4",
                "user": "A",
                "createdAt":"2016-12-23",
          },
          {
                "data": "some data5",
                "user": "B",
                "createdAt":"2016-12-25",
          },
          {
                "data": "some data6",
                "user": "C",
                "createdAt":"2016-12-24",
          },
      ] 
      
      
      
      function getLatestItems(itemArray) {
          var latestItemPerUser = {}, latesItems = [], newDate, oldDate;
      
          for(var i = 0, l = itemArray.length; i < l; ++i) {
              if(!latestItemPerUser[itemArray[i].user]) {
                  latestItemPerUser[itemArray[i].user] = itemArray[i];
              } else {
                  newDate = new Date(itemArray[i].createdAt);
                  oldDate = new Date(latestItemPerUser[itemArray[i].user].createdAt);
      
                  if(oldDate < newDate) {
                      latestItemPerUser[itemArray[i].user] = itemArray[i];
                  }
              }
          }
      
          for(var item in latestItemPerUser) {
              latesItems.push(latestItemPerUser[item]);
          }
      
          return latesItems;
      }
      
      console.log(getLatestItems(myArray));

      【讨论】:

      • 纯javascript的好解决方案。为那些将来来这里寻找一种无需额外库的方法的人投票。 prototype extension 中的 sort() 方法转为 javascript(现在大多数浏览器都内置),可以加快 key 上的排序方法,这可以说是更快了。
      • 谢谢@SamuelJackson。再想一想,如果我们能确定“createdAt”属性总是带有“YYYY-MM-DD”格式,我们就可以去掉 Date 对象。此外,如果我们可以使用 ES2015,代码看起来像
      • ` function getLatestItemsES6(itemArray) { let latestItems = []; for(var item of itemArray.sort((a, b) => a.createdAt obj.user === item.user)) { latestItems.push(item); } } 返回最新项目; } `
      猜你喜欢
      • 1970-01-01
      • 2013-06-24
      • 1970-01-01
      • 2014-10-14
      • 2011-01-13
      • 2011-09-11
      • 1970-01-01
      • 2017-03-17
      相关资源
      最近更新 更多