JavaScript — Группировка массива объектов в поддеревья на основе нескольких свойств

#javascript #arrays

#язык JavaScript #массивы #уменьшить

Вопрос:

Мне нужно решить следующую проблему с помощью функции, которая принимает массив объектов в качестве аргумента и решает все три случая. Учитывая массив объектов, как мне сгруппировать их в подмассивы на основе нескольких условий? Я ищу ошибки в платежной системе и хочу получить массив дублированных транзакций (отсортированных по времени транзакции по возрастанию).

Транзакции считаются дубликатами, если: производитель, сумма, категория точно совпадают, а время между транзакциями составляет менее 45 секунд.

Я ищу решение ES6, и я уверен, что оно будет включать метод .reduce.

Я попытался поработать над этим, выполнив команду reduce, которая дает мне объект, основанный на ключе производителя, и это не тот результат, которого я хотел бы достичь, так как мне нужны поддиапазоны вместо объектов, и мне нужно больше условий, чем просто производитель.

 let groupedArr = data.reduce((accumulator, currentValue) =gt; {  accumulator[currentValue.manufacturer] = [...accumulator[currentValue.manufacturer] || [], currentValue];  return accumulator; }, {});  

Случай 1:

вход:

 const data = [{  id: 3,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:34:30.000Z'  },  {  id: 4,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:34:38.000Z'  },  {  id: 1,  manufacturer: 'mercedes',  amount: 20,  category: 'leasing',  transaction: '2020-03-05T12:00:00.000Z'  },  {  id: 7,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-20T11:00:00.000Z'  },  {  id: 6,  manufacturer: 'mercedes',  amount: 20,  category: 'leasing',  transaction: '2020-03-05T12:00:44.000Z'  },  {  id: 2,  manufacturer: 'volkswagen',  amount: 2,  category: 'credit',  transaction: '2020-03-05T12:00:45.000Z'  },  {  id: 5,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:35:17.000Z'  }, ]  

Ожидаемый результат:

 [[{  id: 3,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:34:30.000Z'  },  {  id: 4,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:34:38.000Z'  },  {  id: 5,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:35:17.000Z'  }],  [{  id: 1,  manufacturer: 'mercedes',  amount: 20,  category: 'leasing',  transaction: '2020-03-05T12:00:00.000Z'  },  {  id: 6,  manufacturer: 'mercedes',  amount: 20,  category: 'leasing',  transaction: '2020-03-05T12:00:44.000Z'  }] ]  

Case 2:

INPUT:

 const data = [{  id: 2,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:34:30.000Z'  },  {  id: 7,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-20T11:00:00.000Z'  }]  

Ожидаемый результат:

 []  

Пояснение: Более 45 секунд между транзакциями должны выводить пустой массив.

Случай 3:

вход:

 const data = [{  id: 2,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:34:30.000Z'  },  {  id: 1,  manufacturer: 'audi',  amount: 40,  category: 'credit',  transaction: '2020-03-02T10:34:40.000Z'  }]  

Ожидаемый результат:

 []  

Пояснение: Менее 45 секунд, но категория другая, поэтому она не считается дубликатом.

Комментарии:

1. «нужно больше условий, чем просто производитель» — Тогда не используйте только ключ производителя в качестве имени свойства. «Мне нужны вложенные массивы вместо объектов» -Просто преобразуйте их после того, как вы сгруппировали записи.

2. Почему id=2 (volkswagen, кредит) не будет включен в выходные данные для случая 1? Смотрите мой ответ для случая 1, я не понимаю, почему вы хотите это исключить. Также, когда есть дубликат (в течение 40 секунд, какой из них сохраняется — тот, который был «раньше» по времени?). Пожалуйста, постарайтесь объяснить это и по возможности сделать свой вопрос немного яснее.

3. Будут ли дубликаты всегда иметь один и тот же идентификатор? (Так как здесь есть две записи для Audi с идентификатором=5, и они находятся в течение 40 секунд) — Плохая практика использовать один и тот же идентификатор дважды, но если это то, что означает дубликат, который также легко решить.

4. отлично, мой ответ должен дать вам то, чего вы сейчас ожидаете.

Ответ №1:

Случай 1:

 function example1(initData, fieldsArr){     const output = data.reduce((aggObj, item) =gt; {  const stringId = fieldsArr.map(key =gt; item[key]).join('_');    if (aggObj[stringId]){  aggObj[stringId].push(item);  }  else {  aggObj[stringId] = [item];  }   return aggObj;  }, {})    const outputNoDups = Object.values(output).map(group =gt; {    const sorted = group.sort((a,b) =gt; new Date(a.transaction) lt; new Date(b.transaction) ? -1 : 1);    return sorted.filter((a, i) =gt; {  if (i == 0) return true;   if (a.amount == sorted[i - 1].amount amp;amp;  new Date(a.transaction) - new Date(sorted[i - 1].transaction) lt;= 45000){  return true;  }    return false;  });  });    return outputNoDups.filter(a =gt; a.length gt; 1); }   console.log(example1(data, ['manufacturer', 'category'])); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 
 lt;script id="initData"gt; const data = [{  id: 3,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:34:30.000Z'  },  {  id: 4,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:34:38.000Z'  },  {  id: 1,  manufacturer: 'mercedes',  amount: 20,  category: 'leasing',  transaction: '2020-03-05T12:00:00.000Z'  },  {  id: 7,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-20T11:00:00.000Z'  },  {  id: 6,  manufacturer: 'mercedes',  amount: 20,  category: 'leasing',  transaction: '2020-03-05T12:00:44.000Z'  },  {  id: 2,  manufacturer: 'volkswagen',  amount: 2,  category: 'credit',  transaction: '2020-03-05T12:00:45.000Z'  },  {  id: 5,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:35:17.000Z'  }, ]; lt;/scriptgt; 

OUTPUT (Case 1):

 [  [  {  "id": 3,  "manufacturer": "audi",  "amount": 40,  "category": "leasing",  "transaction": "2020-03-02T10:34:30.000Z"  },  {  "id": 4,  "manufacturer": "audi",  "amount": 40,  "category": "leasing",  "transaction": "2020-03-02T10:34:38.000Z"  },  {  "id": 5,  "manufacturer": "audi",  "amount": 40,  "category": "leasing",  "transaction": "2020-03-02T10:35:17.000Z"  }  ],  [  {  "id": 1,  "manufacturer": "mercedes",  "amount": 20,  "category": "leasing",  "transaction": "2020-03-05T12:00:00.000Z"  },  {  "id": 6,  "manufacturer": "mercedes",  "amount": 20,  "category": "leasing",  "transaction": "2020-03-05T12:00:44.000Z"  }  ] ]  

Случай 2:

 function example1(initData, fieldsArr){     const output = data.reduce((aggObj, item) =gt; {  const stringId = fieldsArr.map(key =gt; item[key]).join('_');    if (aggObj[stringId]){  aggObj[stringId].push(item);  }  else {  aggObj[stringId] = [item];  }   return aggObj;  }, {})    const outputNoDups = Object.values(output).map(group =gt; {    const sorted = group.sort((a,b) =gt; new Date(a.transaction) lt; new Date(b.transaction) ? -1 : 1);    return sorted.filter((a, i) =gt; {  if (i == 0) return true;   if (a.amount == sorted[i - 1].amount amp;amp;  new Date(a.transaction) - new Date(sorted[i - 1].transaction) lt;= 45000){  return true;  }    return false;  });  });    return outputNoDups.filter(a =gt; a.length gt; 1); }   console.log(example1(data, ['manufacturer', 'category'])); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 
 lt;script id="initData"gt; const data = [{  id: 2,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:34:30.000Z'  },  {  id: 7,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-20T11:00:00.000Z'  }] lt;/scriptgt; 

Случай 3:

 function example1(initData, fieldsArr){     const output = data.reduce((aggObj, item) =gt; {  const stringId = fieldsArr.map(key =gt; item[key]).join('_');    if (aggObj[stringId]){  aggObj[stringId].push(item);  }  else {  aggObj[stringId] = [item];  }   return aggObj;  }, {})    const outputNoDups = Object.values(output).map(group =gt; {    const sorted = group.sort((a,b) =gt; new Date(a.transaction) lt; new Date(b.transaction) ? -1 : 1);    return sorted.filter((a, i) =gt; {  if (i == 0) return true;   if (a.amount == sorted[i - 1].amount amp;amp;  new Date(a.transaction) - new Date(sorted[i - 1].transaction) lt;= 45000){  return true;  }    return false;  });  });    return outputNoDups.filter(a =gt; a.length gt; 1); }   console.log(example1(data, ['manufacturer', 'category'])); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 
 lt;script id="initData"gt; const data = [{  id: 2,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:34:30.000Z'  },  {  id: 1,  manufacturer: 'audi',  amount: 40,  category: 'credit',  transaction: '2020-03-02T10:34:40.000Z'  }] lt;/scriptgt; 

Case 4 (an edge case you hadn’t considered — time is less than 45 but amount is different):

 function example1(initData, fieldsArr){     const output = data.reduce((aggObj, item) =gt; {  const stringId = fieldsArr.map(key =gt; item[key]).join('_');    if (aggObj[stringId]){  aggObj[stringId].push(item);  }  else {  aggObj[stringId] = [item];  }   return aggObj;  }, {})    const outputNoDups = Object.values(output).map(group =gt; {    const sorted = group.sort((a,b) =gt; new Date(a.transaction) lt; new Date(b.transaction) ? -1 : 1);    return sorted.filter((a, i) =gt; {  if (i == 0) return true;   if (a.amount == sorted[i - 1].amount amp;amp;  new Date(a.transaction) - new Date(sorted[i - 1].transaction) lt;= 45000){  return true;  }    return false;  });  });    return outputNoDups.filter(a =gt; a.length gt; 1); }   console.log(example1(data, ['manufacturer', 'category'])); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 
 lt;script id="initData"gt; const data = [{  id: 2,  manufacturer: 'audi',  amount: 40,  category: 'leasing',  transaction: '2020-03-02T10:34:30.000Z'  },  {  id: 1,  manufacturer: 'audi',  amount: 30,  category: 'leasing',  transaction: '2020-03-02T10:34:40.000Z'  }] lt;/scriptgt; 

Комментарии:

1. Я думаю, что ваш вывод непоследователен и неясен. Пожалуйста, уточните, и я легко смогу это решить. Почему id=2 (volkswagen, кредит) не будет включен в выходные данные для случая 1? Я не понимаю, почему вы хотите это исключить. Также, когда есть дубликат (в течение 40 секунд, какой из них сохраняется — тот, который был «раньше» по времени?). Пожалуйста, постарайтесь объяснить это и по возможности сделать свой вопрос немного яснее.

2. Когда в указанных условиях выполняется только одна транзакция (в данном случае у Volkswagen нет подобной транзакции), она должна быть исключена из массива дубликатов (который по определению должен включать 2 или более транзакций).

3. Все транзакции должны быть сохранены, если разница во времени между последней и последующей составляет менее 45 секунд.

4. Пожалуйста, также проверьте всю метку даты. Иногда дни месяца отличаются, поэтому объект не следует включать в массив.

5. Отлично работает в случае 1, но один и тот же код должен работать также для случая 2 и случая 3. Прямо сейчас он выдает одинаковый результат для всех трех случаев, но в случае 2 и 3 должен выводить пустой массив.