使用另一个数组删除/过滤对象数组

Posted

技术标签:

【中文标题】使用另一个数组删除/过滤对象数组【英文标题】:Remove/Filter array of objects with another array 【发布时间】:2021-04-05 08:09:19 【问题描述】:

Array.prototype.remove = function() 
  var what, a = arguments,
    L = a.length,
    ax;
  while (L && this.length) 
    what = a[--L];
    while ((ax = this.indexOf(what)) !== -1) 
      this.splice(ax, 1);
    
  
  return this;
;

var items = [
  title: 'Bokningsbar',
  start: moment("2018-04-05 06:00"),
  end: moment("2018-04-05 07:00"),
  allDay: false
, 
  title: 'Bokningsbar',
  start: moment("2018-04-05 06:00"),
  end: moment("2018-04-05 07:00"),
  allDay: false
, 
  title: 'Bokningsbar',
  start: moment("2018-04-05 06:00"),
  end: moment("2018-04-05 07:00"),
  allDay: false
, 
  title: 'Bokningsbar',
  start: moment("2018-04-05 06:00"),
  end: moment("2018-04-05 07:00"),
  allDay: false
]

var datesToRemove = [
  title: 'Bokningsbar',
  start: moment("2018-04-06 06:00"),
  end: moment("2018-04-06 07:00"),
  allDay: false
];
console.log("Before: " + items.length)
for (var i = 0; i < datesToRemove.length; i++) 
  items.remove(moment(datesToRemove[i].start).format("YYYY-MM-DD HH:mm"));

console.log("After: " + items.length)
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.0/moment.js"&gt;&lt;/script&gt;

我想使用另一个数组作为参考从一个数组中删除对象。两个数组都只包含下面的对象。 datesToRemove 中的所有对应对象都应在 items 数组中删除。我想我可以使用的属性是start。可惜没有Id

这是我目前的代码:

对象看起来像这样:

   var item = 
                            title: 'Bokningsbar',
                            start: moment(datesValue + " " + hourValue.start),
                            end: moment(datesValue + " " + hourValue.end),
                            allDay: false
                        ;

Prototype.remove

       Array.prototype.remove = function () 
        var what, a = arguments,
            L = a.length,
            ax;
        while (L && this.length) 
            what = a[--L];
            while ((ax = this.indexOf(what)) !== -1) 
                this.splice(ax, 1);
            
        
        return this;
    ;

用例:

  for (var i = 0; i < datesToRemove.length; i++) 
                            items.remove(moment(datesToRemove[i].start).format("YYYY-MM-DD HH:mm"));
                        

使用此设置,我收到错误 Cannot read property 'indexOf' of undefined。并且所有的对象都被删除了。这也是一个不工作的例子。但也许它说明了我想要的。如果有人对如何删除对象有更好的建议,请随时提出建议。

非常感谢所有帮助。

【问题讨论】:

【参考方案1】:

据我了解,remove 需要(完全)相同的元素,但通常不能很好地处理复杂的元素。

我建议使用Array.prototype.filter:

var items = [
  title: 'Bokningsbar',
  start: moment("2018-04-05 06:00"),
  end: moment("2018-04-05 07:00"),
  allDay: false
, 
  title: 'Bokningsbar',
  start: moment("2018-04-05 06:00"),
  end: moment("2018-04-05 07:00"),
  allDay: false
, 
  title: 'Bokningsbar',
  start: moment("2018-04-05 06:00"),
  end: moment("2018-04-05 07:00"),
  allDay: false
, 
  title: 'Bokningsbar to remove',
  start: moment("2018-04-06 06:00"),
  end: moment("2018-04-06 07:00"),
  allDay: false
]

var datesToRemove = [
  title: 'Bokningsbar',
  start: moment("2018-04-06 06:00"),
  end: moment("2018-04-06 07:00"),
  allDay: false
];

for (var i = 0; i < datesToRemove.length; i++) 
  var toRemove = datesToRemove[i];
  items = items.filter(function(element) 
    return element.start.valueOf() != toRemove.start.valueOf();
  );


console.log(items);
&lt;script src="https://momentjs.com/downloads/moment-with-locales.min.js"&gt;&lt;/script&gt;

【讨论】:

感谢您抽出宝贵时间为我的问题提出解决方案。最后,我的错误是由用户引起的。我对预期的数据进行了检查,一切都开始按预期工作。再次感谢您!【参考方案2】:

无需扩展本机 Array 原型即可完成此操作。有一些处理 Array 操作的方法已经在 J​​avaScript 中原生可用并且可以解决您的问题。更重要的是,将自定义 Array 方法与解决方案的 for 循环结合使用,很容易产生意外/意外的负面影响,从而限制该解决方案在您的应用程序中的扩展能力。最明显的潜在陷阱是改变您的原始数据结构,极大地限制了可重用性,并且难以轻松准确地调试导致应用程序堆栈进一步下降的问题。这些本机 Array 方法将保持您的原始数据结构完整,在操作期间创建 Array 实例的浅表副本并将它们作为最终结果返回。作为额外的奖励,这些方法可以根据您的需要以多种方式和多次链接在一起。

.map() .filter() .reduce()

另一个常用的Array操作方法是.forEach().forEach() 方法消除了编写和维护凌乱的for 循环的麻烦;使整个代码库更容易阅读,更容易在逻辑上遵循。但是,.forEach() 与之前列出的 3 种 Array 操作方法不同,它不能防止改变所使用的任何原始数据结构。它可以包含在您可以使用提到的其他Array 方法创建的链中。但是,一旦被调用,.forEach() 就会破坏进一步链接您的方法的能力。

.map().filter().reduce()Array 的迭代和操作提供了一种纯粹的函数式方法。虽然所有 3 种方法仍然提供了一种循环 Array 的方法,但每种方法都提供了自己独特的功能和操作目的。首先了解每种方法的不同用途对于了解何时以及如何有效地将它们链接在一起至关重要。

.MAP()

.map() :与.forEach() 类似,此方法循环遍历给定Array 中的每个项目。当循环遍历每个项目的索引时,各个项目将针对提供的操作方法运行。最终结果是一个新的Array,其中每个项目都是循环期间提供的转换函数的产物。使用.map() 要记住的重要注意事项是循环完成后返回的新Array 将始终与使用的原始Array 长度相同。当您需要返回一个新的Array 时,这使得.map() 成为一个很好的选择,其项目值与原始Array 相比反映了所需的转换,并且所有这些都是项目值。

示例:

const originalArr = [1, 2, 3, 4, 5]

const incrementedArr = 
  originalArr.map(
    (n,i,arr) => 
      return n + 4
    
  )

console.log(originalArr)     // console output => [1, 2, 3, 4, 5]
console.log(incrementedArr)  // console output => [5, 6, 7, 8, 9]

在上面的示例中,incrementedArr 常量可以用更简洁的方式重写。为了彻底回答您的问题,我详细地写了它以说明要始终牢记的重要说明。 .map().filter().reduce() 默认提供三个参数。 .map().filter() 的 3 个参数是相同的。他们是:

    项目

    在循环迭代的每个索引处找到的单个项目值

    索引

    循环迭代过程中找到的每个项目的索引值

    数组

    提供对循环使用的原始数组的引用

也就是说,下面是上例中.map() 的更简洁说明:

const originalArr = [1, 2, 3, 4, 5]
const incrementedArr = originalArr.map(n => n + 4)

console.log(originalArr)     // console output => [1, 2, 3, 4, 5]
console.log(incrementedArr)  // console output => [5, 6, 7, 8, 9]  

.FILTER()

.filter() :同样,与.forEach() 一样,此方法循环遍历给定Array 中的每个项目。正如它所做的那样,它将Array 中每个项目的值与提供的条件检查进行比较,为Array 中每个索引处的每个项目返回truefalse。最终输出将是项目的Array,其值都返回相同的Boolean,可以是truefalse,具体取决于提供的条件检查。当您需要返回一个新的Array 时,这使得.filter() 成为完美的选择,与提供的原始Array 相比,它可能需要不同的长度。换句话说,返回的新Array 直接反映了用于过滤掉原始Array 中不再需要的项目值的条件检查。

示例:

const originalArr = [1, 2, 3, 4, 5]
const noEvenNumbersArr = originalArr.filter(n => n%2)

console.log(originalArr)     // console output => [1, 2, 3, 4, 5]
console.log(noEvenNumbersArr)  // console output => [1, 3, 5]

.REDUCE()

.reduce() 方法与提到的其他Array 方法一样,循环遍历给定Array 中的所有项目。它的参数参数不同于.map().filter()。此外,虽然.map().filter() 总是返回Array,但.reduce() 提供了返回功能,而Object 则提供了返回功能。 .reduce() 比讨论的其他两种方法需要更多的时间来正确解释。此外,使用我们使用.map().filter() 讨论过的内容,可以形成一个解决方案来正确解决最初提出的问题。

灵魂

// The Array "items" contains values that need to be removed.

const items = [
   title: 'Bokningsbar',
    start: moment("2018-04-05 06:00"),
    end: moment("2018-04-05 07:00"),
    allDay: false ,

   title: 'Bokningsbar',
    start: moment("2018-04-05 06:00"),
    end: moment("2018-04-05 07:00"),
    allDay: false ,

   title: 'Bokningsbar',
    start: moment("2018-04-05 06:00"),
    end: moment("2018-04-05 07:00"),
    allDay: false , 

   title: 'Bokningsbar',
    start: moment("2018-04-05 06:00"),
    end: moment("2018-04-05 07:00"),
    allDay: false 
]


// The Array "datesToRemove" contains values that match what needs
// to be removed from the"items" Array

const datesToRemove = [
   title: 'Bokningsbar',
    start: moment("2018-04-05 06:00"),
    end: moment("2018-04-05 07:00"),
    allDay: false 
]

.filter() 方法可以处理解决问题所需的一切......几乎。现在的问题具体围绕两件事, 1. 不针对另一个项目数组过滤一个项目数组。相反,您正在尝试根据另一个对象数组过滤一个对象数组。如果这是这里的意图,您可以修改以下内容以检查本机 Object 方法(和迭代器),例如 Object.keys()Object.values()

  const mergeUniqueMatches = (catArr) => catArr.filter((elem, pos, arr) => arr.indexOf(elem) == pos)
  const mergedArr = mergeUniqueMatches(items.concat(datesToRemove))  

mergedArr 记录到控制台会显示一个新的Array。原始数据仍然完好无损。同样,浅拷贝是我们在这里使用的。

现在的诀窍是对mergedArr 进行“重复数据删除”。幸运的是,ES6/7 让这一切变得轻而易举。试试:

const dedupedArr = [...new Set(mergedArr)]  

不过,您需要进一步澄清。您可以随时提供一个密钥(例如生成一个 uuid),并将 items 对象添加到 Array 中,从而让这一切变得更容易。然后在该项目移动到datesToRemoveArray 之后,您需要做的就是关闭实例 ID 密钥 (uuid) 的 .filter() 以安全地删除正确的项目,而不必担心在此过程中改变任何数据。

我会给你这两个最终的选择,

  const destructingOptionWithES6 = 
    ...items,
    ...datesToRemove
  

  const ideallyAllYouShouldHaveToDoIsThisFunction = items.filter(val => !datesToRemove.includes(val))

【讨论】:

首先。感谢您提供这么长的描述性答案。我觉得你的回答很有启发性。我终于找到了导致我的代码中所有错误的原因。这是用户创建的错误。我期望从 SharePoint 获得的日期格式有错误。我实施了日期格式检查,一切都开始按预期工作。由于您的帖子,我将一些迭代更改为map(),这就是为什么我将其标记为我的答案。 不用担心。很高兴这有助于您找到答案。在您的快速重构中使用 .map() 值得称赞。保持数据不可变始终是一个不错的选择。

以上是关于使用另一个数组删除/过滤对象数组的主要内容,如果未能解决你的问题,请参考以下文章

用另一个对象数组过滤对象数组

如何根据另一个对象数组过滤一个对象数组?

对象数组按另一个数组值过滤

无法根据另一个对象数组过滤对象数组

如何在javascript中将对象数组过滤为另一个对象数组? [关闭]

如何通过来自另一个对象数组的数据过滤对象数组? [关闭]