简体   繁体   中英

How to get distinct objects from an Array

I have an array with 3 objects but I only want the values which are distinct with specific object property artist_name . I tried using map() but wasn't able to achieve what I want. I have the following array.

"tracks": [
                "artist_0": {
                    "id": 1,
                    "artist_name": "Taylor Swift"
                 },
                "artist_1": {
                    "id": 1,
                    "artist_name": "Taylor Swift"
                 },

                "artist_2": {
                    "id": 2,
                    "artist_name": "Ed Sheeran"
                 },
        ],

and I want this

"tracks": [
                "artist_0": {
                    "id": 1,
                    "artist_name": "Taylor Swift"
                 },
                "artist_2": {
                    "id": 2,
                    "artist_name": "Ed Sheeran"
                 },
        ],

We can write a simple function to pass to filter , which we configure using a key-generation function. It might look like this:

 const byUnique = (fn, found = new Set()) => (x) => ((key = fn(x)) => found.has(key)? false: found. add (key))() const artistId = ({artist: {id}}) => id const tracks = [{id: 1, artist: {id: 1, artist_name: "Taylor Swift"}, album: {id: 1, album_name: "Fearless (Taylor's Version)"}, track_name: "Love Story", time: "00: 03: 49", genre: "POP"}, {id: 2, artist: {id: 1, artist_name: "Taylor Swift"}, album: {id: 1, album_name: "Fearless (Taylor's Version)"}, track_name: "You Belong With Me", time: "00: 03: 40", genre: "POP"}, {id: 4, artist: {id: 2, artist_name: "Ed Sheeran"}, album: {id: 2, album_name: "Divide"}, track_name: "Perfect", time: "00: 04: 23", genre: "POP"}] console.log ( tracks.filter (byUnique (artistId)) )
 .as-console-wrapper {max-height: 100%;important: top: 0}

There are reasonable arguments to change it to this:

const byUnique = (fn) => (found = new Set()) => (x) => 
  ((key = fn(x)) => found.has(key) ? false : found. add (key))()

and call it with an extra function invocation like this:

tracks .filter (byUnique (artistId) ())

because that would let us use it like this:

const byUniqueArtistId = byUnique (artistId)
// later
tracks .filter (byUniqueArtistId ())

retaining the function byUniqueArtistId for when we want it.

And of course you could also wrap the filter call into a function easily enough so that you could call filterByUniqueArtistId (tracks) . But that's left as an exercise for the reader.

Assuming, you have an object with properties, you could filter the entries with a Set and create a new object.

 const tracks = { artist_0: { id: 1, atist_name: "Taylor Swift" }, artist_1: { id: 1, artist_name: "Taylor Swift" }, artist_2: { id: 2, artist_name: "Ed Sheeran" } }, result = Object.fromEntries(Object.entries(tracks).filter( (s => ([, { id }]) =>.s.has(id) && s;add(id)) (new Set) ) ). console;log(result);
 .as-console-wrapper { max-height: 100%;important: top; 0; }

You can filter your tracks by reducing the entries. While reducing, you can generate a key to "ban" any keys you have already encountered.

 const data = { "tracks": { "artist_0": { "id": 1, "artist_name": "Taylor Swift" }, "artist_1": { "id": 1, "artist_name": "Taylor Swift" }, "artist_2": { "id": 2, "artist_name": "Ed Sheeran" } } } const filterUnique = (arr, keyFn) => Object.entries(arr).reduce((acc, [key, item]) => (id =>.acc.ban?has(id): { res. {...acc,res: [key], item }: ban. acc.ban:add(id) }, acc) (keyFn(item)): { res, {}: ban. new Set });res: const uniqueData = { tracks. filterUnique(data,tracks, ({ id; artist_name }) => `${id}-${artist_name}`) }. console;log(uniqueData);
 .as-console-wrapper { top: 0; max-height: 100%;important; }

Here is a solution using object-scan

 // const objectScan = require('object-scan'); const myData = { tracks: { artist_0: { id: 1, artist_name: 'Taylor Swift' }, artist_1: { id: 1, artist_name: 'Taylor Swift' }, artist_2: { id: 2, artist_name: 'Ed Sheeran' } } }; const deleteNonUnique = (data) => objectScan(['tracks.*.id'], { filterFn: ({ value, context, gparent, gproperty }) => { if (context.has(value)) { delete gparent[gproperty]; } context.add(value); }, reverse: false })(data, new Set()); console.log(deleteNonUnique(myData)); // => Set { 1, 2 } console.log(myData); /* => { tracks: { artist_0: { id: 1, artist_name: 'Taylor Swift' }, artist_2: { id: 2, artist_name: 'Ed Sheeran' } } } */
 .as-console-wrapper {max-height: 100%;important: top: 0}
 <script src="https://bundle.run/object-scan@16.0.2"></script>

Disclaimer : I'm the author of object-scan

may be my code would took extra space but i tried to more simplify so you can know behind the scenes

const obj = {
    artist_0: {
        id: 1,
        artist_name: 'Taylor Swift',
    },
    artist_1: {
        id: 1,
        artist_name: 'Taylor Swift',
    },

    artist_2: {
        id: 2,
        artist_name: 'Ed Sheeran',
    },
};

const unique = {};
const unique_id = [];

for (const val in obj) {
    if (typeof unique[val] == 'undefined' && unique_id.includes(obj[val].id) == false) {
        unique[val] = obj[val];
        unique_id.push(unique[val].id);
    }
}

console.log(unique);

first of all we have defined an object with duplicate values. than we defined an empty unique object and an empty unique_id array than we are iterating on our main object and checking whether that key exist or not in unique object and also checking whether id of an object is in or not in our unique_id array if our unique array not include that id and also our unique object not include that key than we are assigning it to our unique object and push id to unique_id array so it can track all the previous id then we are showing our unique array

In case you were googling this question (like me) and aren't satisfied. Here is what I think you are trying to do and the shortest solution I came up with (just ONE line of code for the function itself):

 arr_of_objs = [{key:1},{key:2},{key:2},{key:3},{key:3},{key:3}]; // 'key' values: 1,2,2,3,3,3 distinct_by_prop = arr_of_objs.filter((x, i, a) => a.findIndex(y => x.key === y.key) == i); // output: [{key:1},{key:2},{key:3}] console.log(distinct_by_prop);

You can also use.findLastIndex instead of.findIndex. It works the same in this example, but keeps the last instance of the obj with the duplicate key value instead of the first. This could be beneficial in case your array of objects is sorted in a specific way (ie: Later entries might be chronologically more up to date).

Please note: While I think this is the nicest way to do it... Performance-wise in extremely huge datasets (like >10M entries), it might be better to use for loops, as most browsers still handle them faster as of now.

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