Как удалить объект из массива объектов на основе максимального значения в javascript
У меня есть массив объектов, который выглядит следующим образом:
[ { 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 }]
Есть ли способ достичь этого, используя что-то вроде Лодаша?
Я думаю, что иду в правильном направлении с этим, но не совсем там и все же:
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 ответов:
Это делает именно то, что вы хотите, в точном порядке, который вы хотите, используя точную библиотеку, которую вы пытаетесь использовать (lodash). Я разбил процесс на следующие этапы:
Шаг 1: сгруппируйте данные. Должен быть массив, содержащий все записи "Bob", и другой массив, содержащий все записи" Joe " и т. д. Я решил пойти дальше и хранить только итоговый результат, а не всю запись.Шаг 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>
Вы можете сделать это довольно просто без подчеркивания / Lodash. Сортировка массива от самого высокого к самому низкому по баллам. Затем используйте массив .фильтр . По мере прохождения фильтра по массиву следите за тем, сколько раз вы видите каждого человека, и начинайте возвращать 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))
Используя только ES6, вы можете использовать 2
reduces. сначала нужно сгруппировать массивы. второй - получить число 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);
Вот решение с лодашем...
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; }
Не совсем то, что вы просили, но стоит подумать:
Лучший способ справиться с этим-реструктурировать ваши данные
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))
Сведите массив к объекту, используя
personprop в качестве ключа. Для каждого человека добавьте к объекту, если ключ не существует или если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);
Это не полный ответ, а всего лишь подсказка. Не используйте
Следующее предложение состоит в том, чтобы выяснить, действительно ли вам нужно изменить массив или просто получить новый массив без некоторых исходных данных массива. Если вам не нужно изменять оригинал, гораздо лучше просто использовать функцию reduce или использовать цикл for для выборочного копирования элементов в новый массив.delete array[index], используйтеarray.splice. Видишь https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice для получения дополнительной информации.
Использование подчеркивания: 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>
Comments