最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

node.js - How to remove unmatched objects from array of objects using javascript or lodash - Stack Overflow

programmeradmin3浏览0评论

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);

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);
Share Improve this question edited Aug 18, 2016 at 9:54 DrakaSAN 7,8538 gold badges56 silver badges96 bronze badges asked Aug 18, 2016 at 5:56 RockRock 75814 silver badges39 bronze badges 6
  • 2 what constitutes a match ? – Mulan Commented Aug 18, 2016 at 6:01
  • @naomik i am trying to match all the properties of first object with the second object properties. – Rock Commented Aug 18, 2016 at 6:04
  • I put your code in a fiddle at jsfiddle.net/kjdx6e4o looks ok to me. Did I miss something? – Dhananjay Commented Aug 18, 2016 at 6:07
  • @Dhananjay the fiddle is working. I dont understand what is the problem – Kaushal Niraula Commented Aug 18, 2016 at 6:10
  • 1 @Jeevan Without the quotes, the data is not valid JSON, perhaps this needs to be fixed on the server ? – Dhananjay Commented Aug 18, 2016 at 6:25
 |  Show 1 more comment

5 Answers 5

Reset to default 8

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.

发布评论

评论列表(0)

  1. 暂无评论