简体   繁体   中英

Find object in collection with fuzzy matching

I have a collection that looks something like this:

const collection = [
    {
        name: 'THIS_ITEM',
        conditions: {
            oneCondition: false,
            anotherCondition: false,
            yourCondition: false,
            myCondition: false
        }
    }, {
        name: 'THAT_ITEM',
        conditions: {
            oneCondition: false,
            anotherCondition: false,
            yourCondition: true,
            myCondition: false
        }
    }, {
        name: 'THOSE_ITEMS',
        conditions: {
            oneCondition: true,
            anotherCondition: false,
            yourCondition: null,
            myCondition: false
        }
    }
];

… and later an object that looks like this:

const condition = {
    oneCondition: true,
    anotherCondition: false,
    yourCondition: true,
    myCondition: false
};

I'm trying to match the condition object against the nested conditions objects in collection to find the one that matches so I can retrieve the name property from the matching entry.

The thing that's throwing me for a loop is the fact that the conditions properties can have “fuzzy” values. By that I mean that if any properties in the source collection are set to true or false they MUST match the values in condition exactly. But if the property in the source collection has a value of null it can match either true or false .

Example:

These would match:

const condition = {
    oneCondition: true,
    anotherCondition: false,
    yourCondition: true,
    myCondition: false
};

const collection = [
    …
    }, {
        name: 'THOSE_ITEMS',
        conditions: {
            oneCondition: true,
            anotherCondition: false,
            yourCondition: null,
            myCondition: false
        }
    }
];

These would not:

const condition = {
    oneCondition: true,
    anotherCondition: false,
    yourCondition: true,
    myCondition: false
};

const collection = [
    …
    }, {
        name: 'THAT_ITEM',
        conditions: {
            oneCondition: false,
            anotherCondition: false,
            yourCondition: true,
            myCondition: false
        }
    }, {
    …
];

Any suggestions? I'm using Lodash but can't seem to imagine any solution without an overly-verbose and nested concoction.

You could use Array#filter with Array#every for the conditions and test against null value as wildcard.

 var collection = [{ name: 'THIS_ITEM', conditions: { oneCondition: false, anotherCondition: false, yourCondition: false, myCondition: false } }, { name: 'THAT_ITEM', conditions: { oneCondition: false, anotherCondition: false, yourCondition: true, myCondition: false } }, { name: 'THOSE_ITEMS', conditions: { oneCondition: true, anotherCondition: false, yourCondition: null, myCondition: false } }], condition = { oneCondition: true, anotherCondition: false, yourCondition: true, myCondition: false }, result = collection.filter(o => Object.keys(condition).every(k => o.conditions[k] === null || o.conditions[k] === condition[k] ) ); console.log(result); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

You can use lodash#filter with lodash#isMatch and lodash#omitBy to match the condition against a collection object's condition that doesn't contain any null values.

const result = _.filter(collection, v => 
  _.isMatch(condition, _.omitBy(v.conditions, _.isNull))
);

 const collection = [ { name: 'THIS_ITEM', conditions: { oneCondition: false, anotherCondition: false, yourCondition: false, myCondition: false } }, { name: 'THAT_ITEM', conditions: { oneCondition: false, anotherCondition: false, yourCondition: true, myCondition: false } }, { name: 'THOSE_ITEMS', conditions: { oneCondition: true, anotherCondition: false, yourCondition: null, myCondition: false } } ]; const condition = { oneCondition: true, anotherCondition: false, yourCondition: true, myCondition: false }; const result = _.filter(collection, v => _.isMatch(condition, _.omitBy(v.conditions, _.isNull)) ); console.log(result); 
 body > div { min-height: 100%; top: 0; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script> 

You can filter the collecting using lodash's _.isMatchWith() :

 const condition = {"oneCondition":true,"anotherCondition":false,"yourCondition":true,"myCondition":false}; const collection = [{"name":"1","conditions":{"oneCondition":true,"anotherCondition":false,"yourCondition":true,"myCondition":false}},{"name":"2","conditions":{"oneCondition":true,"anotherCondition":false,"yourCondition":false,"myCondition":false}},{"name":"3","conditions":{"oneCondition":true,"anotherCondition":false,"yourCondition":null,"myCondition":false}}]; const result = collection.filter(({ conditions }) => _.isMatchWith(condition, conditions, (objValue, othValue) => objValue === null || othValue === null || objValue === othValue) // if at least one of the values is null or they are equal ); console.log(result); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script> 

You can use a combination of lodash .filter() , .every() and .isEqual() methods to loop over the collection and filter only items which have the same conditions as your object:

_.filter(collection, function(c) {
  return _.every(_.keys(condition), function(currentKey) {
    return c.conditions[currentKey] === null ||
      _.isEqual(c.conditions[currentKey], condition[currentKey]);
  });
});

Demo:

 const collection = [{ name: 'THIS_ITEM', conditions: { oneCondition: false, anotherCondition: false, yourCondition: false, myCondition: false } }, { name: 'THAT_ITEM', conditions: { oneCondition: false, anotherCondition: false, yourCondition: true, myCondition: false } }, { name: 'THOSE_ITEMS', conditions: { oneCondition: true, anotherCondition: false, yourCondition: null, myCondition: false } }]; const condition = { oneCondition: true, anotherCondition: false, yourCondition: true, myCondition: false }; var result = _.filter(collection, function(c) { return _.every(_.keys(condition), function(currentKey) { return c.conditions[currentKey] === null || _.isEqual(c.conditions[currentKey], condition[currentKey]); }); }); console.log(result); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 
 <script src="http://underscorejs.org/underscore-min.js"></script> 

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