javascript - 如何按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}
];
尝试获取具有以下条件的数组子集:
- 在每个
id
一小时内,只有最昂贵的值应该在结果集中。 id
如果在一小时内有多个相同的对象等于最昂贵的值,则只放置最早的一个。- 如果整个对象数组中有超过 8
id
个对象,请将其删除。
我知道它是如何在 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 个外观我在这里找到了一些东西,但似乎是红宝石
解决方案
您需要
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>
推荐阅读
- javascript - Autoplay.js 加载时的动画和音频
- python - 如何正确地将包含日期的 csv 中的列转换为 JSON
- python - 使用 python 脚本更新/替换 zip 文件夹中的文件
- ssas - MDX - 每个子组的前 N 个元素
- spring - @Autowired 不应该在没有 @RunWith(SpringRunner.class) 的情况下工作,但可以
- database - 是否可以从不允许 expdp 的损坏安装中恢复 Oracle 数据?
- c++ - 如何定义具有特定格式的类模板以获取模板参数?例如:Fn(Args...)
- performance - Haskell 计算性能
- excel - 变量未加载范围值
- python - AttributeError:“字节”对象在 Python 套接字库上没有“读取”属性