简体   繁体   中英

How do I filter the unique objects from an array if the filter key can have any type of value?

interface FormValues {
    key: string;
    value: any;
}

const array: FormValues[] = [
    {
        key: 'A',
        value: 1 // number
    },
    {
        key: 'A',
        value: 1 // number
    },
    {
        key: 'A',
        value: 'str' // string
    },
    {
        key: 'C',
        value: { a: 1, b: '2' } // object
    },
    {
        key: 'C',
        value: ['a','2'] // array
    },
    {
        key: 'C',
        value: ['a','2'] // array
    }
    {
        key: 'B',
        value: true // boolean
    }
]

I want to filter the objects based on field value , which can have a value of any type.

I tried to do it like this; my solution is not working for nested object checks.

const key = 'value';
const arrayUniqueByKey = [...new Map(array.map(item => [item[key], item])).values()];

output:

    [{
        key: 'A',
        value: 1 // number
    },
    {
        key: 'A',
        value: 'str' // string
    },
    {
        key: 'C',
        value: { a: 1, b: '2' } // object
    },
    {
        key: 'C',
        value: ['a','2'] // array
    },
    {
        key: 'B',
        value: true // boolean
    }]

You need to decide what makes two distinct objects "equal". In JavaScript, all built-in comparisons of objects (which includes arrays) are by reference . That means ['a','2'] === ['a','2'] is false because two distinct array objects exist, despite having the same contents. See How to determine equality for two JavaScript objects? for more information.

I will take the approach that you would like two values to be considered equal if they serialize to the same value via a modified version of JSON.stringify() where the order of property keys are guaranteed to be the same (so {a: 1, b: 2} and {b: 2, a: 1} will be equal no matter how those are stringified). I use a version from this answer to do so:

function JSONstringifyOrder(obj: any, space?: number) {
    var allKeys: string[] = [];
    var seen: Record<string, null | undefined> = {};
    JSON.stringify(obj, function (key, value) {
        if (!(key in seen)) {
            allKeys.push(key); seen[key] = null;
        }
        return value;
    });
    allKeys.sort();
    return JSON.stringify(obj, allKeys, space);
}

And now I can use that to make the keys of your Map :

const arrayUniqueByKey = [...new Map(array.map(
    item => [JSONstringifyOrder(item[key]), item]
)).values()];

And you can verify that it behaves as you'd like:

console.log(arrayUniqueByKey);
/* [{
  "key": "A",
  "value": 1
}, {
  "key": "A",
  "value": "str"
}, {
  "key": "C",
  "value": {
    "a": 1,
    "b": "2"
  }
}, {
  "key": "C",
  "value": [
    "a",
    "2"
  ]
}, {
  "key": "B",
  "value": true
}] */

Playground link to code

This will combine any duplicate keys, creating a new property values to hold the array of combined values (from like keys).

 const array = [{key: 'A', value: 1},{key: 'A', value: 'str'},{key: 'C', value: { a: 1, b: '2'}},{key: 'B',value: true}] const arrayUniqueByKey = [array.reduce((b, a) => { let f = b.findIndex(c => c.key === a.key) if (f === -1) return [...b, a]; else { b[f].values = [...[b[f].value], a.value]; return b } }, [])]; console.log(arrayUniqueByKey)

You can use Array.prototype.reduce() combined with JSON.stringify() and finaly get the result array of values with Object.values()

 const array = [{key: 'A',value: 1,},{key: 'A',value: 1,},{key: 'A',value: 'str',},{key: 'C',value: { a: 1, b: '2' },},{key: 'C',value: ['a', '2'],},{key: 'C',value: ['a', '2'],},{key: 'B',value: true}] const result = Object.values(array.reduce((a, c) => ((a[JSON.stringify(c)] = c), a), {})) console.log(result)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM