Search code examples
javascriptreactjslodash

Lodash filter matching for multiple values in nested objects


I have an array of objects

const obj = [
    {
        "id": 20000,
        "attributes": [
            {
                "name": "Size",
                "value": "38"
            },
            {
                "name": "Color",
                "value": "Blue"
            }
        ]
    },
    {
        "id": 20056,
        "attributes": [
            {
                "name": "Size",
                "value": "38"
            },
            {
                "name": "Color",
                "value": "Brown"
            }
        ]
    }
]

and I have another object to match it against with

const selectedAttributes = {
    "Color": "Brown",
    "Size": "38"
}

the desired result after matching

  • Matching obj with selectedAttributes should return the second entry in obj

What I am trying

Since we cannot provide multiple values in the ._matches property this doesn't work

matchingVariations = _.filter(obj, _.flow(
    _.property('attributes'),
    _.partialRight(_.some, { name: ['Size', 'Color'], value: ['Brown', '38'] })
))

Expected Output

[
    {
    "id": 20056,
    "attributes": [
        {
            "name": "Size",
            "value": "38"
        },
        {
            "name": "Color",
            "value": "Brown"
        }
    ]
    }   
]

const obj = [
    {
        "id": 20000,
        "attributes": [
            {
                "name": "Size",
                "value": "38"
            },
            {
                "name": "Color",
                "value": "Blue"
            }
        ]
    },
    {
        "id": 20056,
        "attributes": [
            {
                "name": "Size",
                "value": "38"
            },
            {
                "name": "Color",
                "value": "Brown"
            }
        ]
    }
]

const selectedAttributes = {
    "Color": "Brown",
    "Size": "38"
}

const selectedAttributes2 = {
    "Size": "38"
}

const matchingVariations = _.filter(obj, _.flow(
    _.property('attributes'),
    _.partialRight(_.some, { name: 'Size', value: '38' })
))

const matchingVariations2 = _.filter(obj, _.flow(
    _.property('attributes'),
    _.partialRight(_.some, { name: ['Size', 'Color'], value: ['Brown', '38'] })
))

console.log(matchingVariations);
console.log(matchingVariations2);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>


Solution

  • Without a library, you could directly use Array#filter to obtain only specific elements.

    Object.entries can be used to get the key-value pairs of the selected attributes, Array#every can be used to ensure that all entries match a given condition, and Array#some can be used to check that the key and value exist in the attributes array of the object.

    const obj=[{id:2000,attributes:[{name:"Size",value:"38"},{name:"Color",value:"Blue"}]},{id:20056,attributes:[{name:"Size",value:"38"},{name:"Color",value:"Brown"}]}];
    const selectedAttributes = {
        "Color": "Brown",
        "Size": "38"
    }
    const selectedAttributes2 = {
        "Size": "38"
    }
    const matches = (arr, attrs)=>arr.filter(x => 
      Object.entries(attrs).every(([k, v])=>
        x.attributes.some(a => a.name === k && a.value === v
    )));
    console.log(matches(obj, selectedAttributes));
    console.log(matches(obj, selectedAttributes2));
    .as-console-wrapper{max-height:100%!important;top:0}