Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is `_.map` returning undefined when I return `false`? [duplicate]

I have a function, which maps an array with underscore. Here is the function:

var services = _.map(userCopy.get('services'), function (service) {
    if (service.service_selected === true) {
        return service;
    }
});

My problem is that when the conditional === false, then I get undefined put into services. Of course I can remove the undefined, but that is ugly and I just want to use map correctly. How do I fix this?

like image 856
jhamm Avatar asked Jan 04 '14 11:01

jhamm


People also ask

How do I stop a map from returning undefined?

To not return undefined values from the map() method, you have to explicitly return a value from the callback function you passed to map() .

Why does js return undefined?

A variable that has not been assigned a value is of type undefined . A method or statement also returns undefined if the variable that is being evaluated does not have an assigned value. A function returns undefined if a value was not returned .


2 Answers

Updated my answer... This solved my problem

    var missingParamsArray = _.map(requiredParams, function (val, param) {
        if (val) {
            if (!req.user[param]) {
                return param;
            }
        }
    });

    var missingParams = _.filter(missingParamsArray, Boolean);

missingParams can also be written as

var missingParams = _.filter(missingParamsArray, function (param) {
       return param !== undefined;
    });
like image 42
Cmag Avatar answered Oct 17 '22 09:10

Cmag


_.map uses whatever the function returns, to build the result. If a function doesn't return anything explicitly, JavaScript returns undefined. That's why the result of _.map has undefined whenever the condition fails.

What you actually need, is a _.filter

var services = _.filter(userCopy.get('services'), function(service) {
    return service.service_selected === true;
});

_.filter accepts a collection and a predicate function. It applies the predicate function to each and every value of the collection and if the function returns true, that element will be included in the result, if the function returns false, that element will be skipped.

Note: You can actually use _.matcher, along with _.filter, like this

var isServiceSelected = _.matcher({
    service_selected: true;
});

var services = _.filter(userCopy.get('services'), isServiceSelected);

This will be very useful if you are going to filter based on service_selected: true condition more than once. If it is just a one-time thing, then _.where shown below would be better alternative.


You can also use _.where, which can return the objects only if the current object contains all the key-value pairs listed, like this

var services = _.where(userCopy.get('services'), {
    service_selected: true;
});

Now, all the service objects will be returned which have service_selected attribute value as true.


Demo:

function display(heading, data) {
    var json = JSON.stringify(data, null, 4);
    document.body.appendChild(document.createElement('h3')).innerHTML = heading;
    document.body.appendChild(document.createElement('pre')).innerHTML = json;
}


var data = [{
    "name": "auth",
    "service_selected": true
}, {
    "name": "authorization",
    "service_selected": false
}, {
    "name": "faltu",
    "service_selected": true
}];


display("_.filter", _.filter(data, function (service) {
    return service.service_selected === true;
}));

display("_.matcher", _.filter(data, _.matcher({
    service_selected: true
})));

display("_.where", _.where(data, {
    service_selected: true
}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
like image 89
thefourtheye Avatar answered Oct 17 '22 09:10

thefourtheye