Exclusive and inclusive filtering of attributes

Posted on

Problem

Here is a simple filter function I have:

function filter (attributes, data, include) {
    if (include) {
        let res = {}
        if (typeof attributes === 'string') {
            res[attributes] = data[attributes]
            return res
        }
        if (attributes.length) {
            attributes.forEach(attr => res[attr] = data[attr])
        }
        return res
    } else {
        if (typeof attributes === 'string') {
            delete data[attributes]
            return data
        }
        if (attributes.length) {
            attributes.forEach(attr => delete data[attr])
        }
        return data
    }
}

As you can see, these parts are same:

if (typeof attributes === 'string') {
    // some code
    return // some variable
}
if (attributes.length) {
    attributes.forEach(attr => /* some code */)
}
return // some variable

What is the best way to remove this duplicated code?

Solution

To @Tushar’s point, those 2 code blocks are not similar. In fact, I would advise you to turn that function in to 2 functions with a better name. Especially since your function optionally depending on include modifies data which is not a good design. It should either always generate a new object, or always modify data. Probably it should always generate a new object.

Furthemore for greater flexibility, I would not check for attributes.length but for attributes.forEach, this way you know for sure the function/method is defined.

In Lodash the first function would be called pick, and the second function would be omit.

Thinking about this, if you are always going to build a new object, you could remove repetitive coding:

function filter(attributes, data, include) {
    'use strict';

    let keys = Object.keys(data),
        res = {};
    //Turn attributes into an array if needed
    attributes = (typeof attributes === 'string') ? [attributes] : attributes;
    keys.forEach(key => {
        //If its an attribute we include, get it
        //If its an attribute we dont exclude, get it
        if ((include && attributes.indexOf(key) != -1) ||
            (!include && attributes.indexOf(key) == -1)) {
            res[key] = data[key];
        }
    });
    return res;
}

Leave a Reply

Your email address will not be published. Required fields are marked *