JavaScript - Группировать массив объектов в подмассивы на основе нескольких свойств - PullRequest
1 голос
/ 25 мая 2020

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

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

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

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

let groupedArr = data.reduce((accumulator, currentValue) => {
 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'
  }]
]

Случай 2:

ВХОД:

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:

INPUT:

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 Ответ

1 голос
/ 25 мая 2020

Случай 1:

function example1(initData, fieldsArr){  
  
  const output = data.reduce((aggObj, item) => {
    const stringId = fieldsArr.map(key => item[key]).join('_');
    
    if (aggObj[stringId]){
      aggObj[stringId].push(item);
    }
    else {
      aggObj[stringId] = [item];
    }

    return aggObj;
  }, {})
  
  const outputNoDups = Object.values(output).map(group => {
  
    const sorted = group.sort((a,b) => new Date(a.transaction) < new Date(b.transaction) ? -1 : 1);
    
    return sorted.filter((a, i) => {
      if (i == 0) return true;

      if (a.amount == sorted[i - 1].amount &&
          new Date(a.transaction) - new Date(sorted[i - 1].transaction) <= 45000){
        return true;
      }
      
      return false;
    });
  });
  
  return outputNoDups.filter(a => a.length > 1);
}  

console.log(example1(data, ['manufacturer', 'category']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script id="initData">
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'
  },
];
</script>

ВЫХОД (Случай 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) => {
    const stringId = fieldsArr.map(key => item[key]).join('_');
    
    if (aggObj[stringId]){
      aggObj[stringId].push(item);
    }
    else {
      aggObj[stringId] = [item];
    }

    return aggObj;
  }, {})
  
  const outputNoDups = Object.values(output).map(group => {
  
    const sorted = group.sort((a,b) => new Date(a.transaction) < new Date(b.transaction) ? -1 : 1);
    
    return sorted.filter((a, i) => {
      if (i == 0) return true;

      if (a.amount == sorted[i - 1].amount &&
          new Date(a.transaction) - new Date(sorted[i - 1].transaction) <= 45000){
        return true;
      }
      
      return false;
    });
  });
  
  return outputNoDups.filter(a => a.length > 1);
}  

console.log(example1(data, ['manufacturer', 'category']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script id="initData">
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'
  }]
</script>

Случай 3:

function example1(initData, fieldsArr){  
  
  const output = data.reduce((aggObj, item) => {
    const stringId = fieldsArr.map(key => item[key]).join('_');
    
    if (aggObj[stringId]){
      aggObj[stringId].push(item);
    }
    else {
      aggObj[stringId] = [item];
    }

    return aggObj;
  }, {})
  
  const outputNoDups = Object.values(output).map(group => {
  
    const sorted = group.sort((a,b) => new Date(a.transaction) < new Date(b.transaction) ? -1 : 1);
    
    return sorted.filter((a, i) => {
      if (i == 0) return true;

      if (a.amount == sorted[i - 1].amount &&
          new Date(a.transaction) - new Date(sorted[i - 1].transaction) <= 45000){
        return true;
      }
      
      return false;
    });
  });
  
  return outputNoDups.filter(a => a.length > 1);
}  

console.log(example1(data, ['manufacturer', 'category']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script id="initData">
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'
  }]
</script>

Случай 4 (крайний случай, который вы не рассматривали - время меньше 45, но количество другое):

function example1(initData, fieldsArr){  
  
  const output = data.reduce((aggObj, item) => {
    const stringId = fieldsArr.map(key => item[key]).join('_');
    
    if (aggObj[stringId]){
      aggObj[stringId].push(item);
    }
    else {
      aggObj[stringId] = [item];
    }

    return aggObj;
  }, {})
  
  const outputNoDups = Object.values(output).map(group => {
  
    const sorted = group.sort((a,b) => new Date(a.transaction) < new Date(b.transaction) ? -1 : 1);
    
    return sorted.filter((a, i) => {
      if (i == 0) return true;

      if (a.amount == sorted[i - 1].amount &&
          new Date(a.transaction) - new Date(sorted[i - 1].transaction) <= 45000){
        return true;
      }
      
      return false;
    });
  });
  
  return outputNoDups.filter(a => a.length > 1);
}  

console.log(example1(data, ['manufacturer', 'category']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script id="initData">
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'
  }]
</script>
...