我有一个看起来像这样的对象数组:
[ { person: 'Fred', scoreTotal: 29 },
{ person: 'Alice', scoreTotal: 34 },
{ person: 'Alice', scoreTotal: 22 },
{ person: 'Mary', scoreTotal: 14 },
{ person: 'Bob', scoreTotal: 33 },
{ person: 'Bob', scoreTotal: 13 },
{ person: 'Bob', scoreTotal: 22 },
{ person: 'Joe', scoreTotal: 28 }]
以及给定人有多个对象的地方->我要保留顶部的“ X”。例如:
a。一个人的前1个结果 结果如下:
[ { person: 'Fred', scoreTotal: 29 },
{ person: 'Alice', scoreTotal: 34 },
{ person: 'Mary', scoreTotal: 14 },
{ person: 'Bob', scoreTotal: 33 },
{ person: 'Joe', scoreTotal: 28 }]
b。一个人的前2个结果 结果如下:
[ { person: 'Fred', scoreTotal: 29 },
{ person: 'Alice', scoreTotal: 34 },
{ person: 'Alice', scoreTotal: 22 },
{ person: 'Mary', scoreTotal: 14 },
{ person: 'Bob', scoreTotal: 33 },
{ person: 'Bob', scoreTotal: 22 },
{ person: 'Joe', scoreTotal: 28 }]
有没有办法使用Lodash之类的东西来实现这一目标?
我认为我正朝着正确的方向前进,但还不完全正确:
for (var i = 0; i < t.length - 1; i++) {
if (
t[i].golfer === t[i + 1].golfer &&
t[i].scoreTotal < t[i + 1].scoreTotal
) {
delete t[i];
}
}
//删除“未定义的条目”
t = t.filter(function(el) {
return typeof el !== "undefined";
});
console.log(t);
答案 0 :(得分:5)
您可以非常简单地执行此操作,而无需Underscore / Lodash。按分数从高到低对数组进行排序。然后使用Array.filter。当筛选器遍历数组时,请跟踪您看到每个人的次数,并在达到所需的最高人数后开始返回false。
let arr = [ { person: 'Fred', scoreTotal: 29 },{ person: 'Alice', scoreTotal: 34 },{ person: 'Alice', scoreTotal: 22 },{ person: 'Mary', scoreTotal: 14 },{ person: 'Bob', scoreTotal: 33 },{ person: 'Bob', scoreTotal: 13 },{ person: 'Bob', scoreTotal: 22 },{ person: 'Joe', scoreTotal: 28 }]
function filterTop(arr, top) {
let counts = {}
return [...arr].sort((a, b) => b.scoreTotal - a.scoreTotal)
.filter(score => (counts[score.person] = (counts[score.person] || 0) +1 ) <= top)
}
console.log(filterTop(arr, 1))
console.log(filterTop(arr, 2))
答案 1 :(得分:4)
仅使用ES6,您可以使用2个reduce
。首先是对数组进行分组。第二个是获取每组 top 的数量。
let arr = [{"person":"Fred","scoreTotal":29},{"person":"Alice","scoreTotal":34},{"person":"Alice","scoreTotal":22},{"person":"Mary","scoreTotal":14},{"person":"Bob","scoreTotal":33},{"person":"Bob","scoreTotal":13},{"person":"Bob","scoreTotal":22},{"person":"Joe","scoreTotal":28}];
let getTop = (a, t) => { //Parameters a = array. t = top
return Object.values(a.reduce((c, v) => { //Group the array using the person property
c[v.person] = c[v.person] || [];
c[v.person].push(v);
return c;
}, {})).reduce((c, v) => {
v.sort((a, b) => b.scoreTotal - a.scoreTotal); //Sort the sub array
c = c.concat(v.slice(0, t)); //Add the top to accumulator
return c;
}, []);
}
let result1 = getTop(arr, 1); //Get top 1
let result2 = getTop(arr, 2); //Get top 2
console.log('Top 1', result1);
console.log('Top 2', result2);
答案 2 :(得分:3)
这是lodash的解决方案...
function pickTopX(data, x) {
let grouped = _.groupBy(data, 'person');
let sorted = _.sortBy(grouped, 'scoreTotal');
let sliced = _.map(sorted, function(pair) {
return _.take(pair, x)
});
let result = _.flatten(sliced);
return result;
}
答案 3 :(得分:3)
这完全按照您要使用的确切库(lodash)中的所需顺序执行所需的操作。我将过程分为以下步骤:
步骤1:对数据进行分组。应该有一个包含所有“ Bob”记录的数组,另一个包含所有“ Joe”记录的数组,依此类推。我决定更进一步,只存储scoreTotal而不是整个记录。
第2步:遍历这些数组中的每一个,按降序对其进行排序,然后切成所需的“顶部”结果。
第3步:使用我们之前的发现过滤原始数据,并且仅在我们拥有与特定人的总得分完全匹配的情况下,才在结果中包括一条记录,并且每个此类得分仅匹配一次,将其从我们的临时数组中删除这样我们就不会得到重复的记录,并且返回的记录比期望的“顶部”记录还要多。
// The magical function
function showTopResults(input, topResults)
{
// Step one: group like data together
var groupedData = _.reduce(input, function(result, value, key) {
if(typeof result[value.person] == 'undefined'){ result[value.person] = []; }
result[value.person].push(value.scoreTotal);
return result;
}, {});
// Step two: loop through person keys, sort it, then grab only the first "x" elements
_.forEach(groupedData, function(value, key) {
value = value.sort(function(a,b){ var n = b - a; return n ? n < 0 ? -1 : 1 : 0}); // first element is largest
groupedData[key] = value.slice(0, topResults); // we only want first x results, so we get largest only
});
// Step three: filter our elements only where we have a match
var filterResults = _.filter(input,function(o){
var idx = _.indexOf(groupedData[o.person],o.scoreTotal);
if( idx > -1)
{
groupedData[o.person].splice(idx, 1); // remove element so we don't get multiple elements of equal value
return true; // We have a match
} else {
return false; // not a match
}
});
return filterResults;
}
// Our input
var input = [
{ person: 'Fred', scoreTotal: 29 },
{ person: 'Alice', scoreTotal: 34 },
{ person: 'Alice', scoreTotal: 22 },
{ person: 'Mary', scoreTotal: 14 },
{ person: 'Bob', scoreTotal: 33 },
{ person: 'Bob', scoreTotal: 13 },
{ person: 'Bob', scoreTotal: 22 },
{ person: 'Joe', scoreTotal: 28 }
];
// Tests using our magical function
console.log(showTopResults(input, 1));
console.log(showTopResults(input, 2));
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
答案 4 :(得分:2)
与您的要求不完全相同,但值得考虑:
处理此问题的最佳方法是重组数据
var people = {},
personArray = [
{ person: 'Fred', scoreTotal: 29 },
{ person: 'Alice', scoreTotal: 34 },
{ person: 'Alice', scoreTotal: 22 },
{ person: 'Mary', scoreTotal: 14 },
{ person: 'Bob', scoreTotal: 33 },
{ person: 'Bob', scoreTotal: 13 },
{ person: 'Bob', scoreTotal: 22 },
{ person: 'Joe', scoreTotal: 28 }
];
//loop person Array to group array by person
//and sort their top scores from best to worst
for(var i = 0; i < personArray.length; i++){
var person = personArray[i].person,
score = personArray[i].scoreTotal,
scores = people[person] || [],
pushed = false;
if(scores.length){
for(var n = 0; n < scores.length; n++){
if(score > scores[n]){
pushed = true;
scores.splice(n, 0, score);
break;
}
}
}
if(!pushed) scores.push(score);
people[person] = scores;
}
console.log(people);
//return top `n` scores for each person from best to worst
function topScores(nScores){
var result = [];
for(var name in people){
if(people.hasOwnProperty(name)){
for(var r = 0; ((r < people[name].length) && (r < nScores)); r++){
result.push({person: name, scoreTotal: people[name][r]});
}
}
}
return result
}
console.log(topScores(2))
console.log(topScores(3))
console.log(topScores(1))
答案 5 :(得分:1)
person
属性作为键,将数组 Reduce变成对象。对于每个添加到对象的人,如果键不存在或者scoreTotal
大于当前的scoreTotal
。使用Object.values()
转换回数组:
const data = [{"person":"Fred","scoreTotal":29},{"person":"Alice","scoreTotal":34},{"person":"Alice","scoreTotal":22},{"person":"Mary","scoreTotal":14},{"person":"Bob","scoreTotal":33},{"person":"Bob","scoreTotal":13},{"person":"Bob","scoreTotal":22},{"person":"Joe","scoreTotal":28}];
const result = Object.values(data.reduce((r, p) => {
if(!r[p.person] || r[p.person].scoreTotal < p.scoreTotal) {
r[p.person] = p;
}
return r;
}, {}));
console.log(result);
答案 6 :(得分:0)
这不是一个完整的答案,只是一个提示。不要使用delete array[index]
,而要使用array.splice
。有关更多信息,请参见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice。
下一个建议是弄清您是否实际上需要修改数组,还是仅获取没有原始数组某些数据的新数组。如果您不需要修改原始文件,那么最好只使用诸如reduce之类的功能,或者使用for循环有选择地将项目复制到新数组中。
答案 7 :(得分:0)
使用下划线:https://underscorejs.org/
const db = [
{ person: 'Fred', scoreTotal: 29 },
{ person: 'Fred', scoreTotal: 10 },
{ person: 'Fred', scoreTotal: 2 },
{ person: 'Alice', scoreTotal: 34 },
{ person: 'Alice', scoreTotal: 5 },
{ person: 'Alice', scoreTotal: 15 },
{ person: 'Alice', scoreTotal: 40 },
{ person: 'Mary', scoreTotal: 23 },
{ person: 'Mary', scoreTotal: 32 },
{ person: 'Mary', scoreTotal: 98 },
{ person: 'Mary', scoreTotal: 4 },
{ person: 'Bob', scoreTotal: 70 },
{ person: 'Bob', scoreTotal: 65 },
{ person: 'Bob', scoreTotal: 35 },
{ person: 'Bob', scoreTotal: 5 },
{ person: 'Joe', scoreTotal: 28 }];
const nOfItens = 2;
const persons = _.map(db, item => item.person);
const names = _.uniq(persons);
let newList = [];
for (var i = 0; names.length > i; i++) {
let filter = _.filter(db, person => person.person === names[i]);
let sort = _.sortBy(filter, num => -num.scoreTotal);
let items = sort.splice(0, nOfItens)
newList = [...newList, ...items]
}
console.log(newList);
<script src="https://underscorejs.org/underscore-min.js"></script>