将数组拆分为包含唯一对象组合的最小子数组

Posted

技术标签:

【中文标题】将数组拆分为包含唯一对象组合的最小子数组【英文标题】:Split array to minimum subarray containing unique object combinations 【发布时间】:2020-12-10 17:43:37 【问题描述】:

我正在做一个项目,其中客户向我们发送一个包含一些对象的数组,请求中也可能有重复的对象。我们根据名为code 的键来确定重复对象。这是一个例子:

[ code: 10, code: 10, code: 10, code: 20, code:30 ]

在上面的示例数组中,我们有 3 个重复的对象,即code: 10

我们需要将此请求发送到第三方 API,该 API 也接受一个数组。问题是,在对这个 API 的单个请求中,我们不能发送重复的对象。所以我们需要将客户端提供的这个数组分解成一个子数组,然后将每个数组发送给API。 如果我们打破上面提供的示例,我们可以得到

[ [ code: 10, code:20, code:30 ], [ code:10 ], [ code:10 ] ]

因此,在每个子数组中,我们都有唯一的对象(不重复) 此外,我们有一个条件,即第一个子数组应包含客户端请求中提供的所有唯一对象。

所以,如果我们有来自客户端的请求,例如

[ code: 10, code: 10, code: 10, code:20, code:20, code:30 ]

那么最小子数组应该是

[ [ code: 10, code: 20, code:30 ], [ code: 10, code:20 ], [ code: 10 ] ]

任何人都可以在这里帮助我如何打破这样的数组? 谢谢

【问题讨论】:

我会遍历所有对象并检查它是否存在于第一个最小数组中。如果不是,则将其放入第一个最小数组中。如果确实存在,请检查下一个。以此类推。 【参考方案1】:

您可以使用reducer 以[[]] 开头,然后尝试将每个项目添加到连续的子数组中,具体取决于它是否可以找到具有与item 相同的codei,然后如果它不能放在任何子数组中,追加一个新子数组。

const input = [ code: 10, code: 10, code: 10, code: 20, code:30 ]

const output = input.reduce((accumulator, item) => 
  let foundSpace = false;

  accumulator.forEach(subArray => 
    if (!foundSpace && !subArray.find(i => i.code == item.code)) 
      subArray.push(item);
      foundSpace = true;
    
  )
  if (!foundSpace) 
    accumulator.push([item])
  
  return accumulator;
, [[]]);

console.log(output)

【讨论】:

【参考方案2】:

这很简单,经过一番摆弄,我找到了答案。我们基本上可以通过 3 个步骤找到子数组

    找到所有唯一的对象,并将它们推送到一个数组中,名称为unique 找出与原始数组的差异 重复,直到有差异为止

const data = [ code: 10, code: 10, code: 10, code:20, code:20, code:30 ];

let temp = data;
const subarray = [];
while (temp.length) 
  // find unique
  let unique = [];
  temp.map(x => unique.filter(a => a.code === x.code).length > 0 ? null : unique.push(x));

  // find difference
  const diff = temp.filter(x => !unique.includes(x));

  // store
  subarray.push(unique);
  temp = diff;

console.log(subarray);

【讨论】:

【参考方案3】:

这是O(n log n) 时间复杂度的解决方案。首先对输入数组进行排序。然后遍历数组以创建输出。如果当前与前一个相同,则将其添加到下一个子数组中。

let input = [ code: 10, code: 10, code: 10, code: 20, code:30 ];

input.sort((a, b) => a.code - b.code);

let arr  = [[]];
let idx  = 0;
let prev = -Infinity;

input.forEach(a => 
    if (a.code !== prev) 
         idx = 0;
     else 
         idx++;
    
    arr[idx] = arr[idx] || [];
    arr[idx].push(a);
    prev = a.code;
);

console.log(arr);

【讨论】:

【参考方案4】:

这是一个有效的解决方案,它只解析一次原始数组。我们将跟踪我们已经在帮助器indices 数组中遇到的代码。这样我们就可以立即查找放置元素的位置。

这样我们可以使用累加器result 简单地减少一个数组,它将包含我们所有的子数组。

const arr = [ code: 10, code: 10, code: 10, code: 20, code:30 ];

// helper array to keep track of where to put elements with given code
let indices = [];

// we're going to accumulate our subarrays into the result array
const result = arr.reduce((result, el) => 
  // on first encounter, put in subarray with index 0
  let index = indices[el.code] ?? 0;
  
  // initialise the subarray if not present, and push
  const subArray = result[index] ?? [];
  subArray.push(el);
  
  // put the subarray into the result array
  result[index] = subArray;
  
  // increment the index so next item with this code will go to the next subarray
  indices[el.code] = ++index;
  
  return result;
, []);

console.log(result);

【讨论】:

以上是关于将数组拆分为包含唯一对象组合的最小子数组的主要内容,如果未能解决你的问题,请参考以下文章

406. 和大于S的最小子数组

将数组拆分为子数组5次,同时在所有子数组中保持唯一对]]

基于创建年份将对象的数组拆分为子数组

根据 NSDictionary 键值将 NSArray 拆分为子数组

44. 最小子数组

LintCode Python 简单级题目 最小子数组和最大子数组和