1
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
    }]
Himanshu Teotia
  • 2,126
  • 1
  • 27
  • 38

3 Answers3

1

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

jcalz
  • 264,269
  • 27
  • 359
  • 360
0

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)
Kinglish
  • 23,358
  • 3
  • 22
  • 43
  • I'm not sure this is correct. OP wants to filter by the value field, and there are two objects with `"key": "A"` with different values, so both should be in your output. – MarksASP Jun 14 '21 at 18:04
  • @MarksASP - thanks, that makes more sense. I updated my answer – Kinglish Jun 14 '21 at 18:24
0

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)
Yosvel Quintero
  • 18,669
  • 5
  • 37
  • 46