首页 > 解决方案 > 如何按key上的groupBy过滤对象数组?

问题描述

我有以下算法基础问题,我正在尝试用 ES6 解决它,但我很困惑groupBy

let data = [
  {id: "2", time: "3/11/2016 02:02:58", value: 7.0},
  {id: "1", time: "3/11/2016 02:12:32", value: 6.5},
  {id: "1", time: "3/11/2016 02:13:11", value: 7.25},
  {id: "4", time: "3/11/2016 02:13:54", value: 8.75},
  {id: "2", time: "3/11/2016 05:02:45", value: 11.0},
  {id: "4", time: "3/11/2016 06:32:42", value: 5.0},
  {id: "2", time: "3/11/2016 06:35:12", value: 2.0},
  {id: "1", time: "3/11/2016 06:45:01", value: 12.0},
  {id: "1", time: "3/11/2016 06:59:59", value: 11.75},
  {id: "2", time: "3/11/2016 07:01:53", value: 1.0},
  {id: "1", time: "3/11/2016 07:02:54", value: 4.5},
  {id: "3", time: "3/11/2016 07:02:54", value: 15.75},
  {id: "6", time: "3/11/2016 07:02:54", value: 14.25},
  {id: "2", time: "3/11/2016 07:03:15", value: 12.0},
  {id: "2", time: "3/11/2016 08:02:22", value: 3.0},
  {id: "2", time: "3/11/2016 09:41:50", value: 4.0},
  {id: "2", time: "3/11/2016 10:02:54", value: 5.0},
  {id: "2", time: "3/11/2016 11:05:35", value: 10.0},
  {id: "2", time: "3/11/2016 13:02:21", value: 6.0},
  {id: "5", time: "3/11/2016 13:02:40", value: 8.0},
  {id: "4", time: "3/11/2016 13:02:55", value: 8.0},
  {id: "5", time: "3/11/2016 13:33:34", value: 8.0},
  {id: "5", time: "3/11/2016 13:42:24", value: 8.0},
  {id: "5", time: "3/11/2016 13:47:44", value: 6.25},
  {id: "5", time: "3/11/2016 14:02:54", value: 4.25},
  {id: "5", time: "3/11/2016 14:03:04", value: 5.25},
  {id: "5", time: "3/11/2016 15:12:55", value: 6.25},
  {id: "2", time: "3/11/2016 16:02:36", value: 8.0},
  {id: "5", time: "3/11/2016 16:22:11", value: 8.5},
  {id: "5", time: "3/11/2016 17:18:19", value: 11.25},
  {id: "5", time: "3/11/2016 18:19:20", value: 9.0},
  {id: "2", time: "3/11/2016 23:59:59", value: 9.0}
];

尝试获取具有以下条件的数组子集:

  1. 在每个id一小时内,只有最昂贵的值应该在结果集中。
  2. id如果在一小时内有多个相同的对象等于最昂贵的值,则只放置最早的一个。
  3. 如果整个对象数组中有超过 8id个对象,请将其删除。

我知道它是如何在 SQL 中轻松实现的,MAX 位于“value”列, GroupBy 位于“time”列,但在这里我对 JS 循环感到困惑。如果可能,请提出一些技巧。

我正在尝试如下,但似乎还有很长的路要走

var newArr = [];
data.forEach(function (el) {
    var findIndex = newArr.findIndex(function (item) {
        return item.time === el.time;
    });
    if (findIndex === -1) {
        newArr.push(el);
    } else if (el.value > newArr[findIndex].value) {
        newArr[findIndex].value = el.value;
        newArr[findIndex].time = el.time;
    } else {
        newArr[findIndex].time = el.time;
    }
});

只是为了使它更清晰输出将是这样的

[
  { id: "4",time: "3/11/2016 02:13:54",value: 8.75 },
  { id: "1", time: "3/11/2016 06:45:01",value: 12.0 },
  { id: "3",time: "3/11/2016 07:02:54",value: 15.75 },
  { id: "4", time: "3/11/2016 13:02:55", value: 8.0}
]

id 记录:“2”和“5”被删除,因为它有超过 8 个外观我在这里找到了一些东西,但似乎是红宝石

标签: javascriptarraysecmascript-6javascript-objects

解决方案


您需要
a) 从 data['time'] 创建日期对象
b) 使用对象以某种方式对数组进行排序。
c) 过滤掉过多的数据。

希望代码能够自我解释。我试图以一种动态的方式来做,这样你就可以在各种年月日里使用它,因为我认为这不仅仅是你想要的时间。

filterOutOldestIds有一个非常丑陋的嵌套 for 循环,但我想不出更好的方法,如果我希望它是动态的但仍然易于管理。

let data = [
  {id: "2", time: "3/11/2016 02:02:58", value: 7.0},
  {id: "1", time: "3/11/2016 02:12:32", value: 6.5},
  {id: "1", time: "3/11/2016 02:13:11", value: 7.25},
  {id: "4", time: "3/11/2016 02:13:54", value: 8.75},
  {id: "2", time: "3/11/2016 05:02:45", value: 11.0},
  {id: "4", time: "3/11/2016 06:32:42", value: 5.0},
  {id: "2", time: "3/11/2016 06:35:12", value: 2.0},
  {id: "1", time: "3/11/2016 06:45:01", value: 12.0},
  {id: "1", time: "3/11/2016 06:59:59", value: 11.75},
  {id: "2", time: "3/11/2016 07:01:53", value: 1.0},
  {id: "1", time: "3/11/2016 07:02:54", value: 4.5},
  {id: "3", time: "3/11/2016 07:02:54", value: 15.75},
  {id: "6", time: "3/11/2016 07:02:54", value: 14.25},
  {id: "2", time: "3/11/2016 07:03:15", value: 12.0},
  {id: "2", time: "3/11/2016 08:02:22", value: 3.0},
  {id: "2", time: "3/11/2016 09:41:50", value: 4.0},
  {id: "2", time: "3/11/2016 10:02:54", value: 5.0},
  {id: "2", time: "3/11/2016 11:05:35", value: 10.0},
  {id: "2", time: "3/11/2016 13:02:21", value: 6.0},
  {id: "5", time: "3/11/2016 13:02:40", value: 8.0},
  {id: "4", time: "3/11/2016 13:02:55", value: 8.0},
  {id: "5", time: "3/11/2016 13:33:34", value: 8.0},
  {id: "5", time: "3/11/2016 13:42:24", value: 8.0},
  {id: "5", time: "3/11/2016 13:47:44", value: 6.25},
  {id: "5", time: "3/11/2016 14:02:54", value: 4.25},
  {id: "5", time: "3/11/2016 14:03:04", value: 5.25},
  {id: "5", time: "3/11/2016 15:12:55", value: 6.25},
  {id: "2", time: "3/11/2016 16:02:36", value: 8.0},
  {id: "5", time: "3/11/2016 16:22:11", value: 8.5},
  {id: "5", time: "3/11/2016 17:18:19", value: 11.25},
  {id: "5", time: "3/11/2016 18:19:20", value: 9.0},
  {id: "2", time: "3/11/2016 23:59:59", value: 9.0}
];

function filterOnMaxValue(arr) {
  let MAX_OCCURANCES = 8;
  var controlObj = {},
      sortedArr = [];   // don't really need this, but using it for added clarity
  
  arr.forEach((el) => {
    controlObj = setHighestValueBasedOnDate(el, controlObj);
  });
  
  sortedArr = filterOutOldestIds(controlObj, MAX_OCCURANCES);

  return sortedArr;
}

function setHighestValueBasedOnDate(el, controlObj) {
  let date = new Date(el.time),
      year = date.getFullYear(),
      month = date.getMonth(),
      day = date.getDay(),
      hour = date.getHours(),
      id = el.id;

  controlObj = createDefaultStructure(id, year, month, day, hour, controlObj);

  let previousEl = controlObj[id][year][month][day][hour];
  controlObj[id][year][month][day][hour] = (previousEl && previousEl.value > el.value) ? previousEl : el;

  return controlObj;
}

function createDefaultStructure(id, year, month, day, hour, controlObj) {
  controlObj[id] = controlObj[id] || {};
  controlObj[id][year] = controlObj[id][year] || {};
  controlObj[id][year][month] = controlObj[id][year][month] || {};
  controlObj[id][year][month][day] = controlObj[id][year][month][day] || {};

  return controlObj;
}

function filterOutOldestIds(controlObj, maxAllowedOccurances) {
  var sortedArr = [],
      idOccurances = {},
      idObj, yearObj, monthObj, dayObj, hourObj;

  for (id in controlObj) {
    idOccurances[id] = -1;
    idObj = controlObj[id];
    
    for (year in idObj) {
      yearObj = idObj[year];
      
      for (month in yearObj) {
        dayObj = yearObj[month];
        
        for (hour in dayObj) {
          hourObj = dayObj[hour];

          for (el in hourObj) {
            idOccurances[id]++;
            
            // this check should honestly be in every for loop, apart from 'id in controlObj'
            if (idOccurances[id] < maxAllowedOccurances) {
              sortedArr.push(hourObj[el]);   
            } else {
              break;
            }
          }
        }
      }
    }
  }

  return sortedArr;
}

let sortedData = filterOnMaxValue(data);
console.log(sortedData);
<p>Trying to get subset of array with below condition</p>

<ul>
  <li>Each Id within each one hour period, only the most expensive value should be in resultset.</li>
  <li>If more than one objects from the same Id equal for the most expensive value in a one hour period, only place the earliest one.</li>
  <li>If there are more than 8 object for an Id in the overall array of objects, remove it.</li>
</ul>


推荐阅读