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
2  
what constitutes a match ? – naomik yesterday
    
@naomik i am trying to match all the properties of first object with the second object properties. – Jeevan yesterday
    
I put your code in a fiddle at jsfiddle.net/kjdx6e4o looks ok to me. Did I miss something? – Dhananjay yesterday
    
@Dhananjay the fiddle is working. I dont understand what is the problem – Kaushal Niraula yesterday
1  
@Jeevan Without the quotes, the data is not valid JSON, perhaps this needs to be fixed on the server ? – Dhananjay yesterday
up vote 7 down vote accepted

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)

share|improve this answer
    
thanks @naomik for your time, and it helped me a lot in understanding the whole problem. thanks a lot – Jeevan yesterday
    
@Jeevan you're very welcome. Be sure to let me know if you have any questions. – naomik yesterday
var res = _.filter(finalResult, function(item) {
  return !_.find(duplicateTestData, {
    batchId: item.batchId,
    licenseId: item.licenseId,
    reportDate: item.reportDate
  });
});
console.log(res);

jsfiddle

share|improve this answer
    
good solution for lodash users +1 – I might recomend _.find(...) === undefined instead of !._find(...) just to be more explicit. – naomik yesterday

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

share|improve this answer
2  
Why not use a Map, at least in the ES6 solution ? – naomik yesterday
    
@naomik, now with map, thanks for the hint. – Nina Scholz yesterday
1  
(r.set(getKey(a)), r) simplifies to r.set(getKey(a)) making Map an even better choice ^_^ – naomik yesterday
    
i didn't know the return of the object. thanks again. – Nina Scholz yesterday
    
you're very welcome ^_^ – naomik yesterday
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]);
}
share|improve this answer

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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.