【问题标题】:JavaScript - Group array of objects into sub-arrays based on multiple propertiesJavaScript - 基于多个属性将对象数组分组为子数组
【发布时间】:2020-09-11 23:19:36
【问题描述】:

我需要使用一个将对象数组作为参数并解决所有三种情况的函数来解决以下问题。 给定一个对象数组,如何根据几个条件将它们分组为子数组?我正在寻找支付系统中的错误,并希望收到一组重复交易(按交易时间升序排序)。

在以下情况下,交易被视为重复:制造商、金额、类别完全相同且交易之间的时间少于 45 秒。

我正在寻找一个 ES6 解决方案,我确信它会包含 .reduce 方法。

我尝试处理它,通过遵循 reduce 给我一个基于制造商密钥的对象,这不是我想要实现的结果,因为我需要子数组而不是对象,并且需要更多的条件而不仅仅是制造商。

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

案例一:

输入:

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:

输入:

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 秒,但类别不同,因此不被视为重复。

【问题讨论】:

  • “需要更多条件而不仅仅是制造商” - 然后不要只使用制造商键作为属性名称。 “我需要子数组而不是对象” - 将条目分组后进行转换。
  • 为什么 id=2 (volkswagen, credit) 不包含在案例 1 的输出中?请参阅我对案例 1 的回答,我不明白您为什么要排除它。此外,当存在重复时(在 40 秒内,保留哪一个 - 时间上“较早”的那个?)。请尝试解释这一点,并尽可能让您的问题更清楚。
  • 重复的ID总是一样吗? (因为这里有两个 id=5 的 Audi 条目,它们在 40 秒内) - 两次使用相同的 id 是不好的做法,但如果这意味着重复也很容易解决。
  • 完美,我的回答应该能满足你现在的期望。

标签: javascript arrays reduce


【解决方案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>

【讨论】:

  • 我认为您的输出不一致且不清楚。请澄清,我可以很容易地解决它。为什么 id=2 (volkswagen, credit) 不包含在案例 1 的输出中?我不明白你为什么要排除它。此外,当存在重复时(在 40 秒内,保留哪一个 - 时间上“较早”的那个?)。请尝试解释一下,并尽可能让您的问题更清楚。
  • 当在指定条件下只有一个交易(在这种情况下大众汽车没有类似的交易),它应该从 Duplicates 数组中排除(根据定义应该包括 2 个或更多交易)。
  • 如果最后一笔交易与下一笔交易的时间差小于45秒,则应保留所有交易。
  • 请同时检查整个日期戳。有时一个月中的日子不同,因此该对象不应包含在数组中。
  • 适用于案例 1,但相同的代码也适用于案例 2 和案例 3。现在它为所有三种案例产生相同的输出,但案例 2 和 3 应该输出一个空数组.
猜你喜欢
  • 2021-03-13
  • 2018-08-17
  • 2019-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-29
相关资源
最近更新 更多