简体   繁体   中英

How to remove unmatched objects from array of objects using javascript or lodash

I am getting two array of objects from the server like this:

var duplicateTestData = [
    { 
        licenseId: 'xxx',
        batchId: '123',
        reportDate: Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time) 
    },
    { 
        licenseId: 'yyy',
        batchId: '124',
        reportDate: Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time) 
    },
    { 
        licenseId: 'aaa',
        batchId: '145',
        reportDate: Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time) 
    }
];

var finalResult = [
    { 
        reportDate: Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time),
        license: {},
        testType: 'P1',
        productType: 'Flower',
        batchId: '123',
        licenseId: 'xxx',
        createType: 'DataUpload' 
    },
    { 
        reportDate: Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time),
        testType: 'P1',
        productType: 'Flower',
        batchId: '124',
        licenseId: 'yyy',
        createType: 'DataUpload' 
    },
    { 
        reportDate: Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time),
        testType: 'P1',
        productType: 'Flower',
        batchId: '145',
        licenseId: 'aaa',
        createType: 'DataUpload' 
    },
    { 
        reportDate: Fri Dec 14 2015 00:00:00 GMT+0530 (India Standard Time),
        testType: 'P1',
        productType: 'Flower',
        batchId: '145',
        licenseId: 'zzz',
        createType: 'DataUpload' 
    }
]

I am trying to fetch only unmatched objects from the finalResult object, the final result would be like this:

[
    { 
        reportDate: Fri Dec 14 2015 00:00:00 GMT+0530 (India Standard Time),
        testType: 'P1',
        productType: 'Flower',
        batchId: '145',
        licenseId: 'zzz',
        createType: 'DataUpload' 
    } 
]  

I am trying this, but am not getting the right result:

for(var j=0;j < duplicateTestData.length;j++){
    for (var i = 0; i < finalResult.length; i++) {
        if (
            (finalResult[i].licenseId == duplicateTestData[j].licenseId)  && 
            (finalResult[i].reportDate == duplicateTestData[j].reportDate) &&
            (finalResult[i].batchId == duplicateTestData[j].batchId)
        ) {
            finalResult.splice(i, 1);
            break;
        }
    }
}

console.log(finalResult);

the easy way out

finalResult.filter(({batchId:a, licenseId:b, reportDate:c}) =>
  duplicateTestData.find(({batchId:x, licenseId:y, reportDate:z}) =>
    a === x && b === y && c === z) === undefined)

=> [ { reportDate: 'Fri Dec 14 2015 00:00:00 GMT+0530 (India Standard Time)',
    testType: 'P1',
    productType: 'Flower',
    batchId: '145',
    licenseId: 'zzz',
    createType: 'DataUpload' } ]

OK, it works , but this is mostly garbage. It doesn't completely and accurately describe the comparison you're trying to make. It's way too specific and it will break as soon as something about your data changes.

Keep reading and we can learn something fun.


object equality for all (keys and values) …

I would begin by making several generic procedures that we can use to better describe the solution to our problem.

What you'll notice about this solution compared to others is that it makes no assumptions about the internals of your data. This solution couldn't care less about the actual key names used in your objects.

That means we won't be touching any batchId , licenseId , or reportDate . Generic procedures can solve everything in this case and the best part about that is you can use them over and over again on any data you wish to process.

 // arrayCompare :: (a -> b -> Bool) -> [a] -> [b] -> Bool const arrayCompare = f=> ([x,...xs])=> ([y,...ys])=> { if (x === undefined && y === undefined) return true else if (! f (x) (y)) return false else return arrayCompare (f) (xs) (ys) } // keys :: Object(k:v) -> [k] const keys = Object.keys // objectCompare :: (v -> v -> Bool) -> Object(k:v) -> Object(k:v) -> Bool const objectCompare = f=> a=> b=> arrayCompare (x=> y=> f (a[x]) (b[y]) && f (a[y]) (b[y])) (keys(a)) (keys(b)) // objectEqual :: Object -> Object -> Bool const objectEqual = objectCompare (x=> y=> x === y) // sample data let xs = [ {a:1,b:10}, {a:2,b:20}, {a:3,b:30} ] let ys = [ {a:1,b:10}, {a:2,b:20}, {a:3,b:30}, {a:4,b:40} ] // return all ys that are not present in xs var result = ys.filter(y=> xs.find(objectEqual(y)) === undefined) console.log(result) // [{a:4,b:40}] 

gotcha !

You will have to adjust this solution somewhat because you're not comparing all object keys. Objects in finalResult have more keys than objects in duplicateTestData so there are zero 1:1 matches.

In simpler words, you want x = {a:1} to be considered a "match" if it is compared against y = {a:1,b:2} , so long as all key:values in x match all key:values in y

If we used the objectEquals comparator above, nothing would be filtered out of finalResult because no object would match an object found in duplicateTestData . Since this is not what you want, let's define a comparator that works for your case …

// subsetObjectEquals :: Object -> Object -> Bool
const subsetObjectEquals = objectCompare (x=> y=> y === undefined || x === y)

// this time use subsetObjectEquals
var result = finalResult.filter(x=>
  duplicateTestData.find(subsetObjectEquals(x)) === undefined)

subsetObjectEquals works a bit differently. I couldn't really think of a better name because this is a somewhat strange comparison. When y is undefined , it means the key for that value is not present in the "subset object" and therefore does not need to be compared

subsetObjectEquals(a,b)
// returns true if all key:value pairs in `a` match all key:value pairs in `b`
// otherwise returns false

full working example

I've attached a full snippet that actually uses the input data included in your question. Expand it here and run it to see it work

 // arrayCompare :: (a -> b -> Bool) -> [a] -> [b] -> Bool const arrayCompare = f=> ([x,...xs])=> ([y,...ys])=> { if (x === undefined && y === undefined) return true else if (! f (x) (y)) return false else return arrayCompare (f) (xs) (ys) } // keys :: Object(k:v) -> [k] const keys = Object.keys // objectCompare :: (v -> v -> Bool) -> Object(k:v) -> Object(k:v) -> Bool const objectCompare = f=> a=> b=> arrayCompare (x=> y=> f (a[x]) (b[x]) && f (a[y]) (b[y])) (keys(a)) (keys(b)) // objectEqual :: Object -> Object -> Bool const objectEqual = objectCompare (x=> y=> x === y) // subsetObjectEquals :: Object -> Object -> Bool const subsetObjectEquals = objectCompare (x=> y=> y === undefined || x === y) // your data var duplicateTestData = [{ licenseId: 'xxx', batchId: '123', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' }, { licenseId: 'yyy', batchId: '124', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' }, { licenseId: 'aaa', batchId: '145', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' } ]; var finalResult = [ { reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', license: {}, testType: 'P1', productType: 'Flower', batchId: '123', licenseId: 'xxx', createType: 'DataUpload' }, { reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '124', licenseId: 'yyy', createType: 'DataUpload' }, { reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '145', licenseId: 'aaa', createType: 'DataUpload' }, { reportDate: 'Fri Dec 14 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '145', licenseId: 'zzz', createType: 'DataUpload' } ] // get all finalResult items that do not subsetObjectEqual items in duplicateTestData var result = finalResult.filter(x=> duplicateTestData.find(subsetObjectEquals(x)) === undefined) console.log(result) 

var res = _.filter(finalResult, function(item) {
  return !_.find(duplicateTestData, {
    batchId: item.batchId,
    licenseId: item.licenseId,
    reportDate: item.reportDate
  });
});
console.log(res);

jsfiddle

You could use a hash table and return a new result set, without the problem of splicing an array while iterating it.

 function getKey(o) { return o.licenseId + '|' + o.reportDate + '|' + o.batchId; } var duplicateTestData = [{ licenseId: 'xxx', batchId: '123', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' }, { licenseId: 'yyy', batchId: '124', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' }, { licenseId: 'aaa', batchId: '145', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' }], finalResult = [{ reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', license: {}, testType: 'P1', productType: 'Flower', batchId: '123', licenseId: 'xxx', createType: 'DataUpload' }, { reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '124', licenseId: 'yyy', createType: 'DataUpload' }, { reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '145', licenseId: 'aaa', createType: 'DataUpload' }, { reportDate: 'Fri Dec 14 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '145', licenseId: 'zzz', createType: 'DataUpload' }], hash = Object.create(null), result = []; duplicateTestData.forEach(function (a) { hash[getKey(a)] = true; }); result = finalResult.filter(function (a) { return !hash[getKey(a)]; }); console.log(result); 

ES6

 function getKey(o) { return o.licenseId + '|' + o.reportDate + '|' + o.batchId; } var duplicateTestData = [{ licenseId: 'xxx', batchId: '123', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' }, { licenseId: 'yyy', batchId: '124', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' }, { licenseId: 'aaa', batchId: '145', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' }], finalResult = [{ reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', license: {}, testType: 'P1', productType: 'Flower', batchId: '123', licenseId: 'xxx', createType: 'DataUpload' }, { reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '124', licenseId: 'yyy', createType: 'DataUpload' }, { reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '145', licenseId: 'aaa', createType: 'DataUpload' }, { reportDate: 'Fri Dec 14 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '145', licenseId: 'zzz', createType: 'DataUpload' }], map = duplicateTestData.reduce((r, a) => r.set(getKey(a)), new Map), result = finalResult.filter(a => !map.has(getKey(a))); console.log(result); 

for (var a = 0; a < duplicateTestData.length; a++) {
  var dt = duplicateTestData[a];
  var dtr = new Date(dt.reportDate + '');

  for (var b = 0; b < finalResult.length; b++) {
    var fr = finalResult[b];
    var frr = new Date(fr.reportDate + '');

    //define your logic how to match two objects
    if (dtr.getTime() !== frr.getTime() &&
      dt.batchId !== fr.batchId) {
      //object matched. remove it from array

      var removed = finalResult.splice(b, 1);
      console.log('items removed', removed);
    }
  }
}

//print finalResult array
for (var c = 0; c < finalResult.length; c++) {
  console.log(finalResult[c]);
}

Using lodash:

duplicateTestData.reduce( _.reject, finalResult );

 var duplicateTestData = [ { licenseId: 'xxx', batchId: '123', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' }, { licenseId: 'yyy', batchId: '124', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' }, { licenseId: 'aaa', batchId: '145', reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)' } ]; var finalResult = [ { reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', license: {}, testType: 'P1', productType: 'Flower', batchId: '123', licenseId: 'xxx', createType: 'DataUpload' }, { reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '124', licenseId: 'yyy', createType: 'DataUpload' }, { reportDate: 'Fri Dec 11 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '145', licenseId: 'aaa', createType: 'DataUpload' }, { reportDate: 'Fri Dec 14 2015 00:00:00 GMT+0530 (India Standard Time)', testType: 'P1', productType: 'Flower', batchId: '145', licenseId: 'zzz', createType: 'DataUpload' } ] console.log( duplicateTestData.reduce( _.reject, finalResult ) ); 
 <script src="https://cdn.jsdelivr.net/lodash/4.15.0/lodash.min.js"></script> 

The core of this is _.reject() , which is the opposite of _.filter() : when passed an object it will use _.matches() to do a partial comparison.

To run it for each of the entries in duplicateTestData we can use .reduce() , the standard Array function. We pass in finalResult as the initialValue. Conveniently the arguments are in the right order, so we don't need an anonymous function! (I should really say that lodash is a very well designed library.) Once reduce() has run through all the entries in duplicateTestData it will return the final filtered result.

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