Удалить дубликаты объектов из массива JSON
У меня есть массив, который выглядит так:
var standardsList = [
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Geometry"},
{"Grade": "Math 1", "Domain": "Counting & Cardinality"},
{"Grade": "Math 1", "Domain": "Counting & Cardinality"},
{"Grade": "Math 1", "Domain": "Orders of Operation"},
{"Grade": "Math 2", "Domain": "Geometry"},
{"Grade": "Math 2", "Domain": "Geometry"}
];
И мне нужно удалить дубликаты, чтобы что-то вроде этого осталось:
var standardsList = [
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Geometry"},
{"Grade": "Math 1", "Domain": "Counting & Cardinality"},
{"Grade": "Math 1", "Domain": "Orders of Operation"},
{"Grade": "Math 2", "Domain": "Geometry"}
];
Я попытался установить underscore.js и использовать._uniq, но это работает только тогда, когда в объекте появляется одна пара key:value
. Кажется, я не могу заставить его работать с несколькими ключами.
Когда я пытаюсь что-то вроде:
var uniqueStandards = _.uniq(standardsList, function(item, key, Domain){
return item.Domain;
});
Я получаю только первые три уникальных значения (по одному на класс). Но мне нужны все уникальные ценности как для класса, так и для домена. Есть ли простой способ передать оба ключа функции _.uniq?
В конечном счете, мне нужен список с каждым уникальным классом в качестве заголовка и уникальных доменов в качестве элементов списка, которые нужно передать в HTML-страницу. Возможно, я ошибаюсь, поэтому, если есть более простой способ достичь этой конечной цели, я открыт для идей.
Спасибо заранее!
Изменить: Получение хороших отзывов и желание уточнить, какова была моя конечная цель. Я пытаюсь создать серию списков в HTML формы:
<div>
<h3>Math K</h3>
<li>Counting & Cardinality</li>
<li>Geometry</li>
</div>
<div>
<h3>Math 1</h3>
<li>Counting & Cardinality</li>
<li>Orders of Operation</li>
</div>
<div>
<h3>Math 2</h3>
<li>Geometry</li>
</div>
Моим оригиналом было создание массива и его толкание в элемент <div>
на странице с $("#divid").append(array)
Ответы
Ответ 1
В конечном счете, мне нужен список с каждым уникальным классом в качестве заголовка и уникальных доменов в качестве элементов списка, которые нужно передать в HTML-страницу. Возможно, я ошибаюсь, поэтому, если есть более простой способ достичь этой конечной цели, я открыт для идей.
Таким образом, вам действительно не нужен этот выходной массив в том формате, о котором вы просили.
В этом случае я бы прямо разрезал преследование простым и эффективным решением:
var grades = {};
standardsList.forEach( function( item ) {
var grade = grades[item.Grade] = grades[item.Grade] || {};
grade[item.Domain] = true;
});
console.log( JSON.stringify( grades, null, 4 ) );
Результирующий объект grades
:
{
"Math K": {
"Counting & Cardinality": true,
"Geometry": true
},
"Math 1": {
"Counting & Cardinality": true,
"Orders of Operation": true
},
"Math 2": {
"Geometry": true
}
}
Интересно, что этот подход очень быстрый. Обратите внимание, что он делает только один проход через входной массив, в отличие от других решений, которым требуется несколько проходов (независимо от того, записываете ли вы их самостоятельно или _.uniq()
делает это для вас). Для небольшого количества элементов это не имеет значения, но хорошо иметь в виду более крупные списки.
И с этим объектом теперь у вас есть все необходимое для запуска любого кода или создания любого другого формата, который вы хотите. Например, если вам нужен точный формат вывода массива, который вы упомянули, вы можете использовать:
var outputList = [];
for( var grade in grades ) {
for( var domain in grades[grade] ) {
outputList.push({ Grade: grade, Domain: domain });
}
}
JSON.stringify( outputList, null, 4 );
Это будет log:
[
{
"Grade": "Math K",
"Domain": "Counting & Cardinality"
},
{
"Grade": "Math K",
"Domain": "Geometry"
},
{
"Grade": "Math 1",
"Domain": "Counting & Cardinality"
},
{
"Grade": "Math 1",
"Domain": "Orders of Operation"
},
{
"Grade": "Math 2",
"Domain": "Geometry"
}
]
Rai спрашивает, как работает эта строка кода:
var grade = grades[item.Grade] = grades[item.Grade] || {};
Это распространенная идиома для извлечения свойства объекта или предоставления значения по умолчанию, если свойство отсутствует. Обратите внимание, что назначения =
выполняются в порядке справа налево. Таким образом, мы могли бы перевести его буквально на использование оператора if
и переменной temp:
// Fetch grades[item.Grade] and save it in temp
var temp = grades[item.Grade];
if( ! temp ) {
// It was missing, so use an empty object as the default value
temp = {};
}
// Now save the result in grades[item.Grade] (in case it was missing)
// and in grade
grades[item.Grade] = temp;
var grade = temp;
Вы можете заметить, что в случае, когда grades[item.Grade]
уже существует, мы берем полученное значение и сохраняем его обратно в одно и то же свойство. Разумеется, это необязательно, и вы, вероятно, не сделали бы этого, если бы вы писали код так. Вместо этого вы упростили бы это:
var grade = grades[item.Grade];
if( ! grade ) {
grade = grades[item.Grade] = {};
}
Это был бы вполне разумный способ написать один и тот же код, и он был бы более эффективным. Это также дает вам возможность сделать более конкретный тест, чем "правность", на которую опирается идиома ||
. Например, вместо if( ! grade )
вы можете использовать if( grade === undefined )
.
Ответ 2
function arrUnique(arr) {
var cleaned = [];
arr.forEach(function(itm) {
var unique = true;
cleaned.forEach(function(itm2) {
if (_.isEqual(itm, itm2)) unique = false;
});
if (unique) cleaned.push(itm);
});
return cleaned;
}
var standardsList = arrUnique(standardsList);
FIDDLE
Это вернет
var standardsList = [
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Geometry"},
{"Grade": "Math 1", "Domain": "Counting & Cardinality"},
{"Grade": "Math 1", "Domain": "Orders of Operation"},
{"Grade": "Math 2", "Domain": "Geometry"}
];
Что именно вы просили?
Ответ 3
Я знаю, что ответов уже много, но лучший из них, который работал для меня для сложной структуры JSON:
var arr = [{ "State": "RWCW", "desc": "WEST", "code": "RWCW", "level": 0, "save": "RWCW : WEST", "attribute1": "", "attribute2": "" }, { "State": "RSCW", "desc": "SOUTHEAST", "code": "RSCW", "level": 0, "save": "RSCW : SOUTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RCSW", "desc": "SOUTHWEST", "code": "RCSW", "level": 0, "save": "RCSW : SOUTHWEST", "attribute1": "", "attribute2": "" }, { "State": "RECW", "desc": "NORTHEAST", "code": "RECW", "level": 0, "save": "RECW : NORTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RWCW", "desc": "WEST", "code": "RWCW", "level": 0, "save": "RWCW : WEST", "attribute1": "", "attribute2": "" }, { "State": "RWCW", "desc": "WEST", "code": "RWCW", "level": 0, "save": "RWCW : WEST", "attribute1": "", "attribute2": "" }, { "State": "RSCW", "desc": "SOUTHEAST", "code": "RSCW", "level": 0, "save": "RSCW : SOUTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RCSW", "desc": "SOUTHWEST", "code": "RCSW", "level": 0, "save": "RCSW : SOUTHWEST", "attribute1": "", "attribute2": "" }, { "State": "RECW", "desc": "NORTHEAST", "code": "RECW", "level": 0, "save": "RECW : NORTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RWCW", "desc": "WEST", "code": "RWCW", "level": 0, "save": "RWCW : WEST", "attribute1": "", "attribute2": "" }, { "State": "RSCW", "desc": "SOUTHEAST", "code": "RSCW", "level": 0, "save": "RSCW : SOUTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RECW", "desc": "NORTHEAST", "code": "RECW", "level": 0, "save": "RECW : NORTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RWCW", "desc": "WEST", "code": "RWCW", "level": 0, "save": "RWCW : WEST", "attribute1": "", "attribute2": "" }, { "State": "RCSW", "desc": "SOUTHWEST", "code": "RCSW", "level": 0, "save": "RCSW : SOUTHWEST", "attribute1": "", "attribute2": "" }, { "State": "RWCW", "desc": "WEST", "code": "RWCW", "level": 0, "save": "RWCW : WEST", "attribute1": "", "attribute2": "" }, { "State": "RCNW", "desc": "MIDWEST", "code": "RCNW", "level": 0, "save": "RCNW : MIDWEST", "attribute1": "", "attribute2": "" }, { "State": "RCSW", "desc": "SOUTHWEST", "code": "RCSW", "level": 0, "save": "RCSW : SOUTHWEST", "attribute1": "", "attribute2": "" }, { "State": "RECW", "desc": "NORTHEAST", "code": "RECW", "level": 0, "save": "RECW : NORTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RCNW", "desc": "MIDWEST", "code": "RCNW", "level": 0, "save": "RCNW : MIDWEST", "attribute1": "", "attribute2": "" }, { "State": "RSCW", "desc": "SOUTHEAST", "code": "RSCW", "level": 0, "save": "RSCW : SOUTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RECW", "desc": "NORTHEAST", "code": "RECW", "level": 0, "save": "RECW : NORTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RCNW", "desc": "MIDWEST", "code": "RCNW", "level": 0, "save": "RCNW : MIDWEST", "attribute1": "", "attribute2": "" }, { "State": "RSCW", "desc": "SOUTHEAST", "code": "RSCW", "level": 0, "save": "RSCW : SOUTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RSCW", "desc": "SOUTHEAST", "code": "RSCW", "level": 0, "save": "RSCW : SOUTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RSCW", "desc": "SOUTHEAST", "code": "RSCW", "level": 0, "save": "RSCW : SOUTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RCNW", "desc": "MIDWEST", "code": "RCNW", "level": 0, "save": "RCNW : MIDWEST", "attribute1": "", "attribute2": "" }, { "State": "RSCW", "desc": "SOUTHEAST", "code": "RSCW", "level": 0, "save": "RSCW : SOUTHEAST", "attribute1": "", "attribute2": "" }, { "State": "RECW", "desc": "NORTHEAST", "code": "RECW", "level": 0, "save": "RECW : NORTHEAST", "attribute1": "", "attribute2": "" }];
var clean = arr.filter((arr, index, self) =>
index === self.findIndex((t) => (t.save === arr.save && t.State === arr.State)))
console.log(clean);
Ответ 4
Возрождение старого вопроса, но я хотел опубликовать итерацию в ответе @adeneo. Этот ответ является полностью общим, но для этого варианта использования он может быть более эффективным (он медленный на моей машине с массивом из нескольких тысяч объектов). Если вы знаете конкретные свойства объектов, которые вам нужно сравнить, просто сравните их напрямую:
var sl = standardsList;
var out = [];
for (var i = 0, l = sl.length; i < l; i++) {
var unique = true;
for (var j = 0, k = out.length; j < k; j++) {
if ((sl[i].Grade === out[j].Grade) && (sl[i].Domain === out[j].Domain)) {
unique = false;
}
}
if (unique) {
out.push(sl[i]);
}
}
console.log(sl.length); // 10
console.log(out.length); // 5
Ответ 5
Решение для вашего случая:
console.log(unique(standardsList));
function unique(obj){
var uniques=[];
var stringify={};
for(var i=0;i<obj.length;i++){
var keys=Object.keys(obj[i]);
keys.sort(function(a,b) {return a-b});
var str='';
for(var j=0;j<keys.length;j++){
str+= JSON.stringify(keys[j]);
str+= JSON.stringify(obj[i][keys[j]]);
}
if(!stringify.hasOwnProperty(str)){
uniques.push(obj[i]);
stringify[str]=true;
}
}
return uniques;
}
Ответ 6
Для меня работает следующее:
_.uniq(standardsList, JSON.stringify)
Это, вероятно, будет медленным для очень длинных списков.
Ответ 7
Мне нужно было сделать некоторую деинтуляцию объектов JSON, поэтому я наткнулся на эту страницу. Тем не менее, я пошел с коротким решением ES6 (нет необходимости в внешних библиотеках), запустив это в Chrome Dev Tools Snippets:
const data = [ /* any list of objects */ ];
const set = new Set(data.map(item => JSON.stringify(item)));
const dedup = [...set].map(item => JSON.parse(item));
console.log(`Removed ${data.length - dedup.length} elements`);
console.log(dedup);
Ответ 8
var standardsList = [
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Geometry"},
{"Grade": "Math 1", "Domain": "Counting & Cardinality"},
{"Grade": "Math 1", "Domain": "Counting & Cardinality"},
{"Grade": "Math 1", "Domain": "Orders of Operation"},
{"Grade": "Math 2", "Domain": "Geometry"},
{"Grade": "Math 2", "Domain": "Geometry"}
];
standardsList = standardsList.filter((li, idx, self) => self.map(itm => itm.Grade+itm.Domain).indexOf(li.Grade+li.Domain) === idx)
document.write(JSON.stringify(standardsList))
Ответ 9
** Следующий метод работает так, как вы хотите. Он фильтрует массив на основе всех значений свойств.
**
var standardsList = [
{ "Grade": "Math K", "Domain": "Counting & Cardinality" },
{ "Grade": "Math K", "Domain": "Counting & Cardinality" },
{ "Grade": "Math K", "Domain": "Counting & Cardinality" },
{ "Grade": "Math K", "Domain": "Counting & Cardinality" },
{ "Grade": "Math K", "Domain": "Geometry" },
{ "Grade": "Math 1", "Domain": "Counting & Cardinality" },
{ "Grade": "Math 1", "Domain": "Counting & Cardinality" },
{ "Grade": "Math 1", "Domain": "Orders of Operation" },
{ "Grade": "Math 2", "Domain": "Geometry" },
{ "Grade": "Math 2", "Domain": "Geometry" }
];
const removeDupliactes = (values) => {
let concatArray = values.map(eachValue => {
return Object.values(eachValue).join('')
})
let filterValues = values.filter((value, index) => {
return concatArray.indexOf(concatArray[index]) === index
})
return filterValues
}
removeDupliactes(standardsList)
Результаты этого
[{Grade: "Math K", Domain: "Counting & Cardinality"}
{Grade: "Math K", Domain: "Geometry"}
{Grade: "Math 1", Domain: "Counting & Cardinality"}
{Grade: "Math 1", Domain: "Orders of Operation"}
{Grade: "Math 2", Domain: "Geometry"}]
Ответ 10
var standardsList = [
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Geometry"},
{"Grade": "Math 1", "Domain": "Counting & Cardinality"},
{"Grade": "Math 1", "Domain": "Counting & Cardinality"},
{"Grade": "Math 1", "Domain": "Orders of Operation"},
{"Grade": "Math 2", "Domain": "Geometry"},
{"Grade": "Math 2", "Domain": "Geometry"}
];
function uniqurArray(array){
var a = array.concat();
for(var i=0; i<a.length; i++) {
for(var j=i+1; j<a.length; j++) {
if(a[i].Grade === a[j].Grade){
a.splice(j--, 1);
}
}
}
return a;
}
uniqurArray(standardsList) // put this js in console and you get uniq object in array
Ответ 11
Используйте этот псевдокод
var standardsList = [
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Counting & Cardinality"},
{"Grade": "Math K", "Domain": "Geometry"},
{"Grade": "Math 1", "Domain": "Counting & Cardinality"},
{"Grade": "Math 1", "Domain": "Counting & Cardinality"},
{"Grade": "Math 1", "Domain": "Orders of Operation"},
{"Grade": "Math 2", "Domain": "Geometry"},
{"Grade": "Math 2", "Domain": "Geometry"}
];
var newArr =[]
for(var i in standardsList){
newArr.push(JSON.stringify(standardsList[i]))
}
var obj = {};
newArr= newArr.filter((item)=>{
return obj.hasOwnProperty(item) ? false : (obj[item] = true);
})
standardsList.length = 0
for(var i in newArr){
standardsList.push(JSON.parse(newArr[i]))
}
console.log(standardsList)
Я выбрал массив образцов, похожий на ваш. Его проще сравнивать объекты, как только вы их набираете. Тогда вам просто нужно сравнить строки.
Ответ 12
Вот короткая однострочная с es6!
const nums = [
"AC8818E1",
"AC8818E1",
"AC8818E1",
"AC8818E1",
"AC8818E1",
"AC9233F2015",
"AC9233F2015",
"AC9233F2015",
"AC8818E1",
"AC8818E1",
"AC8818E1",
"AC8818E1",
"AC8818E1",
"AC8818E2",
"AC8818E2",
"AC8818E2",
"AC8818E2",
"AC9233F2015",
"AC9233F2015",
"AC9233F2015",
"AC9233F2015",
"AC8818E1",
"AC8818E1",
"AC8818E1",
"AC8818E2",
"AC8818E2",
"AC9233F2015",
"AC9233F2015",
"AC8818E1",
"AC8818E1",
"AC8818E1",
"AC8818E2",
"AC8818E2",
"AC8818E2",
"AC8818E2",
"ACB098F25",
"ACB098F25",
"ACB098F25",
"ACB098F25",
"AC8818E2",
"AC8818E2",
"AC8818E1",
"AC8818E1",
"AC8818E1",
]
Set - новый объект данных, представленный в ES6. Потому что Set только позволяет хранить уникальные значения. Когда вы передаете массив, он удалит все повторяющиеся значения.
export const $uniquenums = [...new Set(nums)].sort();