0

So I have an array looks like this:

[
  { date: '2021-07-07' },
  { date: '2021-07-07' },
  { date: '2021-07-07' },
  { date: '2021-07-08' },
  { date: '2021-07-09' },
  { date: '2021-07-10' },
  { date: '2021-07-10' }
];

How can I split into 3 array (What I mean is 1 group for unique dates, and another group for duplicate, but if there more than 1 duplicate group, it should separate into another group)

It will looks like this after split

Array 1
[{"date": "2021-07-07"},{"date": "2021-07-07"},{"date": "2021-07-07"}]
Array 2
[{"date": "2021-07-08"},{"date": "2021-07-09"}]
Array 3
[{"date": "2021-07-10"},{"date": "2021-07-10"}]

Below is my code so far, but it only work if the duplicate on have 1

const findDuplicates = arr => {
  let sorted_arr = arr.slice().sort();
  let result = [];
  for (let i = 0; i < sorted_arr.length - 1; i++) {
    if (sorted_arr[i + 1].date == sorted_arr[i].date) {
      result.push(sorted_arr[i]);
    }
  }
  return result;
};

const filterSame = arr => {
  let temp = findDuplicates(arr);
  const result = arr.filter(date => date.date == temp[0].date);
  return result;
};

const filterUnique = array => {
  let result = array.filter(
    (e, i) => array.findIndex(a => a['date'] === e['date']) === i
  );
  let temp = findDuplicates(array);
  result = result.filter(function(obj) {
    return obj.date !== temp[0].date;
  });
  return result;
};
adiga
  • 34,372
  • 9
  • 61
  • 83
Kanna
  • 163
  • 1
  • 12
  • Why are `2021-07-08` and `2021-07-09` grouped together? – adiga Jul 07 '21 at 08:20
  • @adiga because this is more than just a simple grouping. It's a grouping followed by aggregating everything with only 1 element. – Jamiec Jul 07 '21 at 08:21
  • I understood it as group all the elements by date where there are duplicates, but then have another group for all the unique dates. Thats mainly based on the expected result TBH – Jamiec Jul 07 '21 at 08:22
  • @Jamiec unique ones go to a group and duplicates go to seperate groups? – adiga Jul 07 '21 at 08:23
  • thats how I understood the question yes. It's not particularly well described but theres certainly more to it than a simple group by as per dupe – Jamiec Jul 07 '21 at 08:24
  • What if there is also a `{"date": "2021-07-11"}`? Should it be separate, or should it go with `{"date": "2021-07-8"}]` also? In the latter case, the order of the result should not be important, as there will be a mix. – trincot Jul 07 '21 at 08:28
  • Is the array always sorted? – adiga Jul 07 '21 at 08:30
  • What I mean is 1 group for unique dates, and another group for duplicate, but if there more than 1 duplicate group, it should separate into another group. sorry english is not my first language. – Kanna Jul 07 '21 at 08:31
  • Can you provide the expected result for the example in my previous comment? And can you answer the question whether there is any sorting expected? – trincot Jul 07 '21 at 08:43
  • there is no sort on this array, can you explain to me why sort matter, if there is also a ```{"date": "2021-07-11"}```, it will go with unique group, which is ```{"date": "2021-07-8"}``` as you mention, sorry i am new. – Kanna Jul 07 '21 at 08:54

3 Answers3

2

This could be done in a 2 step process

  1. a typical group by operation based on the date properties
  2. aggregating together all the groups which only have 1 result.

const input = [{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-08"},{"date":"2021-07-09"},{"date":"2021-07-10"},{"date":"2021-07-10"}]

const grouped = input.reduce ( (acc,i) => {
    if(!acc[i.date]) acc[i.date] = []
    acc[i.date].push(i);
    return acc;
},{});

const final = Object.values(Object.entries(grouped).reduce( (acc,[key,values]) => {
   if(values.length>1) {
       acc[key] = values;
   }
   else{
      if(!acc.others) acc.others = [];
      acc.others.push(values[0]);
   }
   return acc
},{}))

console.log(final);

Note that if you added, for example, 2021-07-11 to your original array, this would get lumped in with all the other "unique" elements. This may or may not be what you expected, but was not clear from the question.

Jamiec
  • 133,658
  • 13
  • 134
  • 193
2

You could create a map, keyed by dates, and with as value an empty array. Then populate those arrays. Finally extract the arrays that have more than one element, and add to that result a combined array of those single-element arrays:

function group(data) {
    let map = new Map(data.map(o => [o.date, []]));
    for (let o of data) map.get(o.date).push(o);
    return [
        ...[...map.values()].filter(({length}) => length > 1),
        [...map.values()].filter(({length}) => length == 1).flat()
    ];
}

let data = [{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-08"},{"date":"2021-07-09"},{"date":"2021-07-10"},{"date":"2021-07-10"}];

console.log(group(data));

Explanation

let map = new Map(data.map(o => [o.date, []]));

This creates a Map. The constructor is given an array of pairs. For the example data that array looks like this:

[
   ["2021-07-07", []],
   ["2021-07-07", []],
   ["2021-07-07", []],
   ["2021-07-08", []],
   ["2021-07-09", []],
   ["2021-07-10", []],
   ["2021-07-10", []]
]

The Map constructor will create the corresponding Map, which really removes duplicates. You can imagine it as follows (although it is not a plain object):

{
    "2021-07-07": [],
    "2021-07-08": [],
    "2021-07-09": [],
    "2021-07-10": []
}

Then the for loop will populate these (four) arrays, so that the Map will look like this:

{
    "2021-07-07": [{date:"2021-07-07"},{date:"2021-07-07"},{date:"2021-07-07"}],
    "2021-07-08": [{date:"2021-07-08"}],
    "2021-07-09": [{date:"2021-07-09"}],
    "2021-07-10": [{date:"2021-07-10"},{date:"2021-07-10"}]
}

In the return statement the Map values are converted to an array twice. Once to filter the entries that have more than 1 element:

[
    [{date:"2021-07-07"},{date:"2021-07-07"},{date:"2021-07-07"}],
    [{date:"2021-07-10"},{date:"2021-07-10"}]
]

...and a second time to get those that have 1 element:

[
    [{date:"2021-07-08"}],
    [{date:"2021-07-09"}],
]

The second array is flattened with flat():

[
    {date:"2021-07-08"},
    {date:"2021-07-09"},
]

The final result concatenates the first array (with duplicate dates) with the flattened array (with unique dates), using the spread syntax (...)

trincot
  • 317,000
  • 35
  • 244
  • 286
  • I so prefer this answer to my own. Nice! – Jamiec Jul 07 '21 at 08:39
  • it works, it very good if you put some comment so i can know what's going on, but anyway thank you. – Kanna Jul 07 '21 at 08:44
  • 1
    Added explanation to my answer. Hope it clarifies it. – trincot Jul 07 '21 at 08:57
  • you make my day @trincot, I appreciate it. – Kanna Jul 07 '21 at 09:02
  • Hi @trincot, i just checked and see that the result for ```[[{"date":"2021-07-11"}],[{"date":"2021-07-08"}]]``` how can i edit and make it group in just 1 array? – Kanna Jul 07 '21 at 09:20
  • like this ```[{"date":"2021-07-11"},{"date":"2021-07-08"}]``` – Kanna Jul 07 '21 at 09:21
  • 1
    This input has a nested structure (array of arrays of objects), while your question has an array of objects. So in this case you must first apply `.flat()` to your input, before passing it to the function in my answer. Of course, it is important that your input has a consistent *shape*. – trincot Jul 07 '21 at 09:23
  • I am using the same input but in my console.log it's showing like this ```[[{ date: '2021-07-07' },{ date: '2021-07-07' },{ date: '2021-07-07' }],[{ date: '2021-07-09' },{ date: '2021-07-09' },{ date: '2021-07-09' }],[[[Object]],[[Object]]]]``` my input : ```[{"date":"2021-07-11"},{"date":"2021-07-07"},{"date":"2021-07-09"},{"date":"2021-07-08"},{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-09"},{"date":"2021-07-09"}]``` – Kanna Jul 07 '21 at 09:33
  • 1
    Did you implement the call to `.flat()` that I have in my answer (I added it a bit after posting the initial answer)? – trincot Jul 07 '21 at 09:34
0

Another option is to sort the array before grouping. If the current date being looped isn't the same as its neighbors, then it doesn't have duplicates.

const input = [{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-08"},{"date":"2021-07-09"},{"date":"2021-07-10"},{"date":"2021-07-10"}]

input.sort((a,b) => a.date.localeCompare(b.date))

const grouped = input.reduce((acc, o, i, arr) => {
  const key = o.date === arr[i-1]?.date || o.date === arr[i+1]?.date
                ? o.date
                : 'lonely'
                
  acc[key] ||= []
  acc[key].push(o);
  return acc;
}, {});

console.log(Object.values(grouped));
adiga
  • 34,372
  • 9
  • 61
  • 83