JavaScript地图,然后过滤唯一的数组项。

6

我知道如何分别完成这两件事,但我相信一定有一种方法可以将它们结合起来。

我有一个类别数组,我从一个对象数组中提取它们:

 this.videoCategories = this.videos.map(v => v.category);

当然,这个数组中有重复的元素。因此,现在我执行以下操作:

this.uniqueVideoCategories = this.videoCategories.filter((item, index) => {
  return this.videoCategories.indexOf(item) === index;
});

这个方法可以正常工作,我得到一个没有重复项的分类数组。但我试图学习并使代码更简洁一些,将它们串联起来,但这不起作用-返回空数组。

  constructor(private videoService: VideoService) {
    this.videos = videoService.getVideos();
    this.videoCategories = this.videos
      .map(v => v.category)
      .filter((item, index) => {
        return this.videoCategories.indexOf(item) === index;
      });
    console.log(this.videoCategories);
  }

https://dev59.com/PXI-5IYBdhLWcg3wO1rl - epascarello
6个回答

5

filter() 中,您正在检查对象数组中的索引。您可以使用 filter() 方法的第三个参数,在 map() 之后将是新创建的数组。

 constructor(private videoService: VideoService) {
    this.videos = videoService.getVideos();
    this.videoCategories = this.videos
      .map(v => v.category)
      .filter((item, index, arr) => {
        return arr.indexOf(item) === index;
      });
    console.log(this.videoCategories);
  }

使用 Set 可以替代 filter()indexOf(),以去除重复项。这将使时间复杂度为 O(N)

constructor(private videoService: VideoService) {
    this.videos = videoService.getVideos();
    this.videoCategories = [...new Set(this.videos.map(v => v.category))]
    console.log(this.videoCategories);
  }

1
过滤器更快 https://blog.usejournal.com/performance-of-javascript-array-ops-2690aed47a50 - William Lohan
我总是忘记那第三个参数。谢谢。 - Steve

3
有时候解决问题的关键在于选择正确的数据结构。ES6引入了Set,它只包含唯一的对象。
然后你只需要执行:
this.videoCategories = new Set(this.videos.map(v => v.category))

翻译:独特性将由浏览器实现处理,而不是使您的代码库混乱。

1
筛选更快 https://blog.usejournal.com/performance-of-javascript-array-ops-2690aed47a50 - William Lohan
1
@WilliamLohan 这个例子将 Set 转换回数组...这不是一个有效的比较。你可能是正确的,但这不是这种用例的有效测试。 - charlietfl
@WilliamLohan 这篇文章比较了使用 ... 扩展集合和使用 filter,我并没有建议扩展它。无论如何,如果性能很重要,我会建议使用普通的 for 循环。中间的 .map 是一个额外的分配和数组循环,比 Set.filter 的细微差别更重要。 - Tomáš Zato
哦,我明白了。我刚刚谷歌了一下哪个更快,因为filter-indexOf是我的“goto”,看到这里的Set解决方案,我很好奇。 - William Lohan

2

var videos = [
  { category: 'category1', title: 'Category 1'},
  { category: 'category1', title: 'Category 1'},
  { category: 'category1', title: 'Category 1'},
  { category: 'category2', title: 'Category 2'},
  { category: 'category2', title: 'Category 2'}
];
var categoryVideos =
  videos
    .map(v => v.category)
    .filter((item, index, arr) => arr.indexOf(item) === index);
    
console.log(categoryVideos);

Array.prototype.filter

Syntax

var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])

参数

callback

函数是一个谓词,用于测试数组的每个元素。如果保留该元素,则返回true,否则返回false。它接受三个参数:

  • element:正在处理的数组中的当前元素。
  • index:(可选)正在处理的当前元素在数组中的索引。
  • array:(可选)调用过滤器的数组。
  • thisArg:(可选)执行回调时要使用的this值。

返回值

一个新数组,其中包含通过测试的元素。如果没有元素通过测试,则将返回一个空数组。


注:Original Answer翻译成“最初的回答”。

0

数组为空是因为在过滤数组时,return this.videoCategories.indexOf(item) === index;中的字段this.videoCategories为空。

尝试一下:

this.videoCategories = this.videos
    .map(v => v.category)
    .filter((item, index, array) => {
        return array.indexOf(item) === index;
    });

项,索引,数组!我总是忘记传递数组。谢谢。 - Steve

0
this.videoCategories = this.videos
.map(v => v.category)
.filter((item, index, array) => 
    array.indexOf(item) === index;
);

如果您不使用括号,只是一行代码,那么您不需要返回值。


0

我会这样做

constructor(private videoService: VideoService) {
  this.videos = videoService.getVideos();
  const categories = this.videos.map(v => v.category);
  this.videoCategories = [...new Set(categories)];
  console.log(this.videoCategories);
}

欢迎来到SO,并感谢您的回答!请注意,通常认为解释代码运行原理是一个好的实践。毕竟,我们不只是一个编写代码的服务平台,更是一个学习交流的地方。另外,请参考如何撰写好的回答获取更多相关信息。 - Tijmen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接