简体   繁体   中英

Lodash to filter out an array of objects

I am trying to filter out a nested array of objects using lodash which is pretty straightforward but I am looking to avoid multiple calls.

I am looking to create 2 array of objects using a single lodash call/function. Looking to find object property "$isMultiAccount" if it exists put the whole object into one result set and if not put it to another ruleset.

Currently I am doing this with Lodash "has and filter" for first and for other ",has" which means same object is looped twice , as object is relatively large its creating bottleneck for speed

https://repl.it/repls/HomelyExpensiveTruetype

const item = {
  "domains": [
    {
      "id": "dm11022",
      "information":{
        "description": "Customer",
        "owner": {
          "primary":{
            "name": "James",
            "phone": "NA"
          },
          "others": [
            {
              "$isMultiAccount": "./Yes"
            }
          ]
        }
      }
    },
    {
      "id": "dm12022",
      "information":{
        "description": "Customer",
        "owner": {
          "primary":{
            "name": "James",
            "phone": "NA"
          },
          "others": [
            {
              "$isMultiAccount": "./No"
            }
          ]
        }
      }
    },
    {
      "id": "dm12022",
      "information":{
        "description": "Customer",
        "owner": {
          "primary":{
            "name": "James",
            "phone": "NA"
          },
          "others": [
            {
              "conf": {
                  "isVpnBased":{
                    "accountType": "Primary"
                  }
              }
            }
          ]
        }
      }
    }


  ]
}
/*
Expected result
  output1 = [
        {
      "id": "dm11022",
      "information":{
        "description": "Customer",
        "owner": {
          "primary":{
            "name": "James",
            "phone": "NA"
          },
          "others": [
            {
              "$isMultiAccount": "./Yes"
            }
          ]
        }
      }
    },
    {
      "id": "dm12022",
      "information":{
        "description": "Customer",
        "owner": {
          "primary":{
            "name": "James",
            "phone": "NA"
          },
          "others": [
            {
              "$isMultiAccount": "./No"
            }
          ]
        }
      }
    }
  ]

// $isMultiAccount account do not exist in this object
 output2 = [
       {
      "id": "dm12022",
      "information":{
        "description": "Customer",
        "owner": {
          "primary":{
            "name": "James",
            "phone": "NA"
          },
          "others": [
            {
              "conf": {
                  "isVpnBased":{
                    "accountType": "Primary"
                  }
              }
            }
          ]
        }
      }
    }
 ]


 */
const item = {
"domains": [
{
  "id": "dm11022",
  "information":{
    "description": "Customer",
    "owner": {
      "primary":{
        "name": "James",
        "phone": "NA"
      },
      "others": [
        {
          "$isMultiAccount": "./Yes"
        }
      ]
    }
  }
},
{
  "id": "dm12022",
  "information":{
    "description": "Customer",
    "owner": {
      "primary":{
        "name": "James",
        "phone": "NA"
      },
      "others": [
        {
          "$isMultiAccount": "./No"
        }
      ]
    }
  }
},
{
  "id": "dm12022",
  "information":{
    "description": "Customer",
    "owner": {
      "primary":{
        "name": "James",
        "phone": "NA"
      },
      "others": [
        {
          "conf": {
              "isVpnBased":{
                "accountType": "Primary"
              }
          }
        }
      ]
    }
  }
}
]
}
const [areMulti, areNotMulti] = _.reduce(item.domains, (current, next) => {
  return _.has(next, ‘information.owner.others.$isMultiAccount’)
    ? [current[0].concat(next), current[1]]
    : [current[0], current[1].concat(next)];
}, [[], []]);
console.log(areMulti);
console.log(areNotMulti);

Since item.domains.information.owner.others is an array, you need to tackle it as follows:

let multi = []; 
let notMulti = [];
_.each(item.domains, function (obj) {
   if (obj.information.owner.others.length && _.has(obj.information.owner.others[0], '$isMultiAccount'))
      multi.push(obj);
   else
      notMulti.push(obj);
});
console.log(multi);
console.log(notMulti);

Unfortunately, you have to iterate over the domains array as well ass on the owner.others array to determine if the object with specific key sits inside.

So the algorithm has O(n*m) complexity.

If you ask for a lodash function seems that the partition method is what you're looking for

As docs says:

Creates an array of elements split into two groups, the first of which contains elements predicate returns truthy for, the second of which contains elements predicate returns falsey for. The predicate is invoked with one argument: (value).

So it will be like:

_.partition(
  item.domains,
  e => _.some(
    _.get(e, 'information.owner.others'),
    el => _.has(el,"$isMultiAccount")
  )
);

Watch out - some hack available!

However, if the you're 100% sure that the element you're looking for will be always at specific index (for example it is supposed to be always as first element - so index 0) you can limit the algorithm to have linear complexity O(n) as only the size of the domains array will matter in terms of performance.

The hackish solution assuming fixed array index=0:

_.partition(
  item.domains,
  e => _.has(e, 'information.owner.others.0.$isMultiAccount')
);

NOTE Using lodash makes code a bit easier to read but of course it creates some performance overhead anyway.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM