从 JSON 数组中删除重复对象

IT技术 javascript jquery underscore.js
2021-02-07 02:50:09

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

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)

6个回答
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);

小提琴

这将返回

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"}
];

这正是您所要求的?

如果我有很长的 json 列表(至少 500 个 json,每个 10 个属性),你对性能部分怎么说。
2021-03-17 02:50:09

我知道已经有很多答案,但对我来说对复杂的 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);

您可以直接在 chrome 浏览器控制台中尝试并根据需要进行编辑。

我希望这可以帮助别人。

这很棒!我认为使用内置函数比使用 foreach 更有效。:)
2021-03-18 02:50:09

最终,我需要一个列表,其中每个唯一等级作为标题,唯一域作为列表项传递到 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 );

这将记录:

[
    {
        "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语句和一个临时变量:

// 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 ).

@Rai 当然,我在答案的末尾添加了一个注释来解释它。
2021-03-15 02:50:09
我希望生成一些 html,以 Grade 作为标题,并在其下方的无序列表中生成该等级独有的域。我在想该数组会让我在它们之间注入一些 html 标签并创建一个 html 字符串。像这样 ["<div>Grade1<li>Domain1</li><li>Domain2</li></div><div>Grade2<li>Domain1</li> 等等..."]
2021-03-19 02:50:09
但是,如果您想要原始问题中询问的输出格式,那很容易。我用几行代码更新了答案以生成该格式。
2021-03-23 02:50:09
您在这里询问的数组格式听起来根本不实用。当等级和域像这样混合时,您如何区分它们?合作起来会很困难。很容易将此grades对象转换为您想要的任何格式,或者直接使用数据并根据需要调用任何类型的函数。但是不要沉迷于像数组这样的数据格式,它不会工作。要问的真正问题是你想用那里的数据做什么。
2021-04-02 02:50:09
这非常接近我所需要的。您将如何修改它以使结果成为 [等级,域,域,等级,域,域,等级,域] 形式的简单数组?
2021-04-07 02:50:09

我需要对 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);

重温一个老问题,但我想在@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
最佳答案在这里,imo
2021-03-13 02:50:09