为什么JavaScript的map函数会返回undefined?

167

我的代码

 var arr = ['a','b',1];
 var results = arr.map(function(item){
                if(typeof item ==='string'){return item;}  
               });
这将产生以下结果。
["a","b",undefined]

我不想在结果数组中看到undefined。我该怎么做?


5
因为你只会返回字符串类型的内容,所以最后一项返回的是 undefined。如果不是一个字符串类型的内容,你希望返回什么?是一个空字符串吗? - BenM
3
如果它不是字符串,我不想返回任何东西。甚至不返回undefined。 - Akshat Jiwan Sharma
7
看起来我之前使用了错误的方法来做这件事。我会像建议的那样使用过滤器。 - Akshat Jiwan Sharma
4
jQuery.map 实际上足够聪明,不会在生成的数组中包括 undefined 和 null 值。 - Donald T
@JackB 哦,这是一个不错的发现。我会查看他们在源代码中如何实现它。谢谢。 - Akshat Jiwan Sharma
1
@DonaldTaylor 在这种情况下,更清晰的做法是使用过滤器方法来实现你想要的目标,即过滤掉元素,而不是将一个值映射到另一个值。 - Ikke
13个回答

247

如果项目不是字符串,则您的函数没有返回任何内容。在这种情况下,函数返回undefined,这就是您在结果中看到的。

map函数用于将一个值映射到另一个值,但是看起来你实际上想要过滤数组,而map函数并不适合这种情况。

您实际需要的是一个filter函数。它接受一个函数作为参数,并根据该函数返回true或false来确定是否将该项包含在结果数组中。

var arr = ['a','b',1];
var results = arr.filter(function(item){
    return typeof item ==='string';  
});

1
这很有道理。我没有使用.map,而是使用.filter...你怎么知道的?!O.o谢谢^.^ - DigitalDesignDj

53

过滤器适用于这种特定情况,其中项未被修改。但是在许多情况下,当您使用map时,您想对传递的项目进行某些修改。

如果这是您的意图,您可以使用reduce

var arr = ['a','b',1];
var results = arr.reduce((results, item) => {
    if (typeof item === 'string') results.push(modify(item)) // modify is a fictitious function that would apply some change to the items in the array
    return results
}, [])

3
谢谢 - map 的结果为带有 undefined 的数组。filter 只返回项或不返回。这很完美。 - Zach Smith

21
自从 ES6 开始,filter 支持类似于 LINQ 的 pointy 箭头符号表示法,因此可以简化为以下一行代码。
['a','b',1].filter(item => typeof item ==='string');

19

我的解决方案是在 map 之后使用 filter。

这应该支持每种 JS 数据类型。

例如:

const notUndefined = anyValue => typeof anyValue !== 'undefined'    
const noUndefinedList = someList
          .map(// mapping condition)
          .filter(notUndefined); // by doing this, 
                      //you can ensure what's returned is not undefined

19

你可以按照以下逻辑进行实现。 假设你想要一个数值数组。

let test = [ {name:'test',lastname:'kumar',age:30},
             {name:'test',lastname:'kumar',age:30},
             {name:'test3',lastname:'kumar',age:47},
             {name:'test',lastname:'kumar',age:28},
             {name:'test4',lastname:'kumar',age:30},
             {name:'test',lastname:'kumar',age:29}]

let result1 = test.map(element => 
              { 
                 if (element.age === 30) 
                 {
                    return element.lastname;
                 }
              }).filter(notUndefined => notUndefined !== undefined);

output : ['kumar','kumar','kumar']

11
如果你必须使用map返回自定义输出,你仍然可以与filter结合使用。
const arr = ['a','b',1]

const result = arr.map(element => {

  if(typeof element === 'string')
    return element + ' something'

}).filter(Boolean) // this will filter out null and undefined

console.log(result) // output: ['a something', 'b something']

太棒了!我不知道你可以这样使用 filter。所以,Array.map 总是会返回一个与原数组中相同数量的项目组成的数组,而 Array.filter 不会吗? - Narxx
.filter(Boolean) 哦,是的。 - timray

9

只有当前元素是字符串时,您才返回一个值。否则,分配一个空字符串可能就足够了:

var arr = ['a','b',1];
var results = arr.map(function(item){
    return (typeof item ==='string') ? item : '';  
});

当然,如果你想过滤任何非字符串元素,你不应该使用 map()。相反,你应该考虑使用 filter() 函数。


5
如果存在一个数字,这会返回一个空字符串。 - Prasath K

6
var arr = ['a','b',1];
var results = arr.filter(function(item){
                if (typeof item ==='string') {return item;}  
               });

1

我经常遇到这种情况,即在过滤后类型仍然是string | number。因此,为了扩展这些解决方案并包括类型安全性,您可以使用用户定义的类型保护。 https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates

const result = ['a','b',1].filter((item) => typeof item ==='string');
// result is typed as (string | number)[]

使用用户定义的类型保护来提高类型安全性
const result = ['a','b',1].filter((item): item is string => typeof item ==='string');
// result is now typed as string[]

1
问题:
问题在于arr.map()会对arr数组长度进行完整迭代,即map()方法将循环与arr的长度一样多次,无论您在其中有什么条件,所以如果您在其中定义了一个条件例如 if(typeof item ==='string'){return item;},即使条件不满足,map()也会被强制继续循环直到完成整个arr的循环,因此如果条件未满足,则其余元素将返回undefined
解决方案:
解决方案一:如果要在满足条件时返回数组中的整个项,则可以使用arr.filter(),因此过滤器将为迭代返回整个项,例如,如果您有以下对象数组
const arr = [{name: "Name1", age: 25}, {name: "Name2", age: 30}, {name: "Name3", age: 25}]

当满足条件时,您希望返回整个对象,如下例所示。
const filteredItems = arr.filter((item)=>{if(item.age === 25){return true}})

console.log(filteredItems) //results: [{name: "Name1", age: 25}, {name: "Name3", age: 25}]

结论:如果条件满足,filter()方法将返回整个数组中的所有项目。
解决方案二:如果你想仅返回数组中对象的特定数据(或整个对象或任何形状的数据),例如仅返回数组中的名称而不包括年龄,则可以这样做。
const namesOnly = arr.map((item)=>{if(item.age === 25){return item.name}})

console.log(namesOnly) //results: ["Name1, udefined, "Name3"]

现在要去掉 undefined,只需像下面这样在结果上使用 filter() 方法即可。

const namesOnly = arr.map((item)=>{if(item.age === 25){return item.name}}).filter((item)=> !!item)

console.log(namesOnly) //results: ["Name1, "Name3"]

结论: map() 方法会返回一个特定定义的数据数组,如果条件不满足则返回undefined。因此,我们可以使用filter()方法来移除undefined

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