172

My code

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

This gives the following results

["a","b",undefined]

I don't want undefined in the results array. How can I do it?

6
  • 5
    Because you don't return anything unless it's a string. Therefore, the last item returns undefined. What do you expect to return if it's not a string? An empty string? Commented Apr 16, 2013 at 12:28
  • 3
    @BenM if it is not a string I want nothing returned.Not even undefined. Commented Apr 16, 2013 at 12:34
  • 7
    Looks like I was using the wrong method to do this.I will use filter as suggested. Commented Apr 16, 2013 at 12:35
  • 4
    jQuery.map is actually smart enough to not include undefined and null values in the resulting array. Commented May 1, 2013 at 14:13
  • 1
    @DonaldTaylor In this case though it's more clear to use the filter method to what you want to do, filter out elements, not map one value to another. Commented Sep 18, 2015 at 7:55

13 Answers 13

251

You aren't returning anything in the case that the item is not a string. In that case, the function returns undefined, what you are seeing in the result.

The map function is used to map one value to another, but it looks like you actually want to filter the array, which a map function is not suitable for.

What you actually want is a filter function. It takes a function that returns true or false based on whether you want the item in the resulting array or not.

var arr = ['a','b',1];
var results = arr.filter(function(item){
    return typeof item ==='string';  
});
Sign up to request clarification or add additional context in comments.

1 Comment

This makes quite a bit of sense. I was not .map'ing I was .filter'ing... How did you know?! O.o Thanks ^.^
56

Filter works for this specific case where the items are not modified. But in many cases when you use map you want to make some modification to the items passed.

if that is your intent, you can use 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
}, [])

1 Comment

Thanks - map results in arrays with undefined. filter only returns the item or not. this is perfect
21

You can implement like a below logic. Suppose you want an array of values.

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']

1 Comment

This requires 2 iterations over the test list though, which might not be ideal if the list is large.
20

Since ES6 filter supports pointy arrow notation (like LINQ):

So it can be boiled down to following one-liner.

['a','b',1].filter(item => typeof item ==='string');

Comments

20

My solution would be to use filter after the map.

This should support every JS data type.

example:

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

Comments

12

If you have to use map to return custom output, you can still combine it with 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']

2 Comments

Brilliant! I didn't know you can use filter like this. So, Array.map will always return an array with the same number of items as the original array, and Array.filter will not?
.filter(Boolean) ooooh yes.
10

You only return a value if the current element is a string. Perhaps assigning an empty string otherwise will suffice:

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

Of course, if you want to filter any non-string elements, you shouldn't use map(). Rather, you should look into using the filter() function.

1 Comment

This returns an empty string if there exists an number
6
var arr = ['a','b',1];
var results = arr.filter(function(item){
                if (typeof item ==='string') {return item;}  
               });

Comments

2

I run into this quite frequently where the type after filtering will still be string | number. So, to expand upon these solutions and include type safety you can use a user-defined type guard. 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)[]

Better type safety using user-defined type guard

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

Comments

1

The problem: the issue is arr.map() will do a full iteration of arr array length, i.e. map() method will loop as much as the length of arr is, no matter what condition you have inside it, so if you defined a condition inside it e.g. if(typeof item ==='string'){return item;} even if the condition is not happening, the map() will be forced to keep looping until finishing the looping of the whole arr so it will give you undefined for the rest of elements if the condition is not met.

The solutions:

Solution One: if you want to return the whole item in the array when the condition is met, you can use arr.filter() so the filter will return the whole item for the iteration e.g. if you have array of objects like bellow

const arr = [{name: "Name1", age: 25}, {name: "Name2", age: 30}, {name: "Name3", age: 25}]

and you want to return the whole objects when the condition is met like example below

const filteredItems = arr.filter((item)=>{if(item.age === 25){return true}})

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

conclusion: filter() method returns an array of the whole items in it if the condition is met.

Solution Two: if you want to return only a specific data of the objects (or the whole object or any shape of data) in array i.e. if you want to return only the names in array without the ages, you can do this

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

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

now to remove the undefined you just use filter() method on the results like below

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

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

conclusion: map() method returns an array of specifically defined data in the return, and returns undefined if the condition is not met. so then we can use filter() method to remove the undefined.

Comments

1

Map is used when you want to produced new modified array from the original array. the simple answer may be for someone

      var arr = ['a','b',1];
      var results = arr.filter(function(item){
      // Modify your original array here
     return typeof item ==='string';  
     }).filter(a => a);

1 Comment

Is that not just a copy of a 9 years old upvoted answer? If not, please consider adding some explanation on how it is different, and how it is better than existing answers.
0

If you use it like this, your problem will be solved. Also, you will have a clean and short code

var _ = require('lodash'); //but first, npm i lodash --save
var arr = ['a','b',1];
var results = _.compact(
    _.map(arr, function(item){
        if(_.isString(item)){return item;}
    }
); //false, null, undefined ... etc will not be included

with ES6...

const _ = require('lodash'); //but first, npm i lodash --save
const arr = ['a','b',1];
const results = _.compact(
    _.map(arr, item => {
        if(_.isString(item)){return item;}
    }
);

Comments

0

You can filter records with .map easily using below example code

const datapoints = [
    {
        PL_STATUS: 'Packetloss',
        inner_outerx: 'INNER',
        KPI_PL: '97.9619'
    },
    {
        PL_STATUS: 'Packetloss',
        inner_outerx: 'OUTER',
        KPI_PL: '98.4621',
    },
    {
        PL_STATUS: 'Packetloss',
        inner_outerx: 'INNER',
        KPI_PL: '97.8770',
    },
    {
        PL_STATUS: 'Packetloss',
        inner_outerx: 'OUTER',
        KPI_PL: '97.5674',

    },
    {
        PL_STATUS: 'Packetloss',
        inner_outerx: 'INNER',
        KPI_PL: '98.7150',
    },
    {
        PL_STATUS: 'Packetloss',
        inner_outerx: 'OUTER',
        KPI_PL: '98.8969'
    }
];
const kpi_packetloss_inner: string[] = [];
datapoints.map((item: { PL_STATUS: string; inner_outerx: string; KPI_PL: string }) => {
    if (item.PL_STATUS === 'Packetloss' && item.inner_outerx === 'INNER') {
        kpi_packetloss_inner.push(item.KPI_PL);
    }
})
console.log(kpi_packetloss_inner);

2 Comments

This question is asked more than 9 years ago and it has an accepted answer. Please add some details about the reason you are adding a new answer
This question was marked as a duplicate of another question, so that's why I added my answer here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.