如何基于JavaScript中的最大值从对象数组中删除对象

时间:2018-07-10 02:52:39

标签: javascript lodash

我有一个看起来像这样的对象数组:

[ { 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);

8 个答案:

答案 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>