3

The following code is from this SO post. The code works as expected but I am unable to understand how the code is working. The accepted answer for this post is very similar but I was unable to understand that as well.

const array = [
 { class: "second", fare: "a" }, 
 { class: "second", fare: "b" }, 
 { class: "first", fare: "a" }, 
 { class: "first", fare: "a" }, 
 { class: "second", fare: "a" }, 
 { class: "first", fare: "c" }
];

console.log(unique(array, ['class', 'fare']));

function unique(arr, keyProps) {
 const kvArray = arr.map(entry => {
  const key = keyProps.map(k => entry[k]).join('|');
  return [key, entry];
 });
 const map = new Map(kvArray);
 return Array.from(map.values());
}

when I console entry[k] I see all the values but after joining, the see only the unique values.

Can someone please help me understand this.

Maddy
  • 2,025
  • 5
  • 26
  • 59
  • [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) [Array.join](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join) – epascarello Sep 24 '19 at 13:50
  • 1
    The function makes Map keys from the values of the requested properties. A Map can only have one entry for a given key, so you end up without duplicates. – Pointy Sep 24 '19 at 13:51

1 Answers1

3

What you misunderstood is where the duplicates are removed.

You think the duplicate were removed by the join but they are still there at this moment.

Here's the proof :

const array = [
 { class: "second", fare: "a" }, 
 { class: "second", fare: "b" }, 
 { class: "first", fare: "a" }, 
 { class: "first", fare: "a" }, 
 { class: "second", fare: "a" }, 
 { class: "first", fare: "c" }
];

unique(array, ['class', 'fare']);

function unique(arr, keyProps) {

 const kvArray = arr.map(entry => {
  const key = keyProps.map(k => entry[k]).join('|');
  return [key, entry];
 });
 
 console.log(kvArray)
 
 const map = new Map(kvArray);
 return Array.from(map.values());
}

the line which actually remove duplicates is this one

const map = new Map(kvArray);

a Map is a structure with no duplicate keys, where you give an array to the map constructor it need to look like this : [[ 1, 'one' ],[ 2, 'two' ]] where the first value of each pair (here the digits) becomes the map keys while the second value is taken as the value in the map (here the string)

As map can't have duplicate keys, when a key is met for the second time the value replace the previous value, effectively removing duplicates keys

the Map is then transformed back into an array with

Array.from(map.values())

A more commented version of the snippets :

const array = [
 { class: "second", fare: "a" }, 
 { class: "second", fare: "b" }, 
 { class: "first", fare: "a" }, 
 { class: "first", fare: "a" }, 
 { class: "second", fare: "a" }, 
 { class: "first", fare: "c" }
];

console.log(unique(array, ['class', 'fare']));

function unique(arr, keyProps) {

 /* 
    for each entry of the array transform it into
    [
      "entry.class|entry.fare",
      entry
    ]
 */
 const kvArray = arr.map(entry => { // for each entry
  const key = keyProps   // for each prop (here "class" and "fare")

    .map(k => entry[k])  // get them from the entry effectively creating
                         // an array looking like this [entry.class, entry.fare]
                         
    .join('|');          // join them with "|" creating the string "class|fare"
  return [key, entry];   // return the array ["class|fare", entry]
 });
 /* 
  here the kvArray looks like
  [
    [
      "second|a",
      { class: "second", fare: "a" }
    ],
    [ 
      "second|b",
      { class: "second", fare: "b" },
    ],
    [
      "first|a",   
      { class: "first", fare: "a" }, 
    ],
    ...
  ]
 */
 
 const map = new Map(kvArray); // create a Map using mechanism explained above
 /*
  the above line is equivalent to
  const map = new Map()
  kvArray.forEach( el => map.set(el[0], el[1]) )
 */
 
 // map.values return an Iterator with only the values of the map (the keys are lost)
 // meaning we only get back the objects we had at the start
 // using Array.from(...) to get an array out of this iterator
 return Array.from(map.values());
}

the different revelant documentations Array.map, Array.join, Map, Map.values, Array.from

jonatjano
  • 3,576
  • 1
  • 15
  • 20