JavaScript中indexOf的工作原理引起困惑

5

我是JS的新手,正在尝试学习如何正确地使用JS中的indexOf。也就是说,如果你看下面的代码:

var sandwiches = ['turkey', 'ham', 'turkey', 'tuna', 'pb&j', 'ham', 'turkey', 'tuna'];

var deduped = sandwiches.filter(function (sandwich, index) {
    return sandwiches.indexOf(sandwich) === index;
});

// Logs ["turkey", "ham", "tuna", "pb&j"]
console.log(deduped);

我试图去除重复项,但有两个问题想问。首先,在这里 return sandwiches.indexOf(sandwich) === index; 为什么需要使用 "== index"。其次,由于 indexOf 返回索引,如 0、1 或 2 ...,那么为什么当我们 console.log(deduped) 时,我们得到的是名字数组而不是索引数组。希望你明白我的意思。

5个回答

6
你可以使用Javascript数组的filter方法,该方法接受一个返回布尔值的函数。 filter函数根据传递进来的函数对每个条目进行操作,并返回一个新的数组。
如果函数返回true,则在新数组中包含该条目,否则将其丢弃。
由于函数检查一个条目的indexOf是否等于当前索引位置,因此只有第一次出现的条目才会返回true。
所有重复的元素都会被舍弃,因为它们不是indexOf找到的第一个索引。

嗨,马里奥,我的意思是为什么我们要将 indexOf 函数返回的值与 index 进行比较来实现元素去重呢? - Dickens
indexOf返回元素的索引,如果找到则返回该索引,否则返回-1。如果有重复项,则indexOf每次都会返回第一次出现的索引,因此如果您的重复项在索引5上,即您在索引3处有第一次出现,则会得到:3 === 5,这是错误的,并且将丢弃重复项。 - Mario Santini
我的最后一个问题是,由于indexOf返回的是数组中元素的索引而不是名称,在我们三明治的情况下,最终indexOf返回0、1、3、4。然后,由于这些索引被放入数组中(该数组将被filter返回),为什么我们会在返回的数组中得到元素的名称,而不是[0,1,3,4]?谢谢。 - Dickens
过滤函数返回新数组,该数组将包含原始数组中的元素,而作为过滤器参数提供的函数仅用于决定哪些原始数组元素将插入新数组。该函数返回一个布尔值。 - Mario Santini
谢谢Mario的友善解释:),很抱歉您错过了我的倒数第二个问题。只需要您的确认,就这样了,谢谢:) - Dickens
显示剩余2条评论

3
  1. 由于逻辑是从数组中删除重复项,在你的例子中,"turkey" 是重复项。"turkey" 存在于位置 0、2、6,因此每次调用 indexOf("turkey") 都会返回 0,因为 indexOf 函数返回一个子字符串的第一次出现位置。所以对于位置为 2 和 6 的元素,条件不满足,则不会将该元素返回。

  2. 这就是 JavaScript 中 filter 的工作原理。它评估条件并返回 true 或 false,表示是否将元素包含在新数组中,在你的例子中,条件是 return sandwiches.indexOf(sandwich) === index;


2

如果使用箭头符号,也许基本逻辑一眼就能看出来:

const deduped = myArray => myArray.filter((x, i) => myArray.indexOf(x) === i);

关键点在于indexOf返回x第一个出现的索引。对于该出现,比较的结果将为true,因此过滤器将保留该元素。对于任何后续出现,比较将为false,过滤器将拒绝它。

嗨John,感谢你的友善回答,我只想问一个问题。由于indexOf返回的是数组中元素的索引而不是名称,在我们三明治的情况下,最终indexOf返回0、1、3、4。然后,由于这些索引被放入数组中(该数组将被filter返回),为什么我们在返回的数组中得到元素的名称而不是[0,1,3,4]?谢谢。 - Dickens
@Dickens indexOf和比较 myArray.indexOf(x) === i 都不是整个函数的返回值。整个函数返回的是 myArray.filter( .... ) 。你现在过于关注筛选条件。当你对字符串数组进行筛选时,当然会得到一个字符串数组。 - John Coleman

1
  1. ===(恒等)和==(相等)之间的区别:如果比较的值的类型不同,则===将返回false,而==将尝试将值转换为相同的类型。因此,在比较某些具有已知类型的值的情况下,最好使用===。(http://www.c-point.com/javascript_tutorial/jsgrpComparison.htm

  2. 您会得到一个名称数组,而不是索引数组,因为Array.filter不会改变值,而只会过滤它们。在您的情况下,筛选函数是return sandwiches.indexOf(sandwich) === index;,它返回truefalse。如果您想在去重后获取项目的索引,则在筛选后使用map

a.filter(...).map(function(item, idx) {return idx;})

0

@Dikens,indexOf会返回元素的索引,如果找到了该元素。如果未找到该元素,则返回-1

在您的情况下,您正在过滤数组并将值存储在 deduped 中。这就是为什么它显示为一个数组。

如果您在过滤函数中控制 indexOf ,则它将记录元素的索引。

例如:

var deduped = sandwiches.filter(function (sandwich, index) {
   console.log(sandwiches.indexOf(sandwich));
   return sandwiches.indexOf(sandwich) === index;
});

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