Kanna
Kanna

Reputation: 213

Split array with condition on element

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;
};

Upvotes: 0

Views: 2661

Answers (3)

trincot
trincot

Reputation: 350776

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 (...)

Upvotes: 2

adiga
adiga

Reputation: 35253

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));

Upvotes: 0

Jamiec
Jamiec

Reputation: 136154

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.

Upvotes: 2

Related Questions