Well, I wouldn't really look for a library for something so simple. Instead try building a solution yourself.
You could first JSON.parse() any strings to convert them into objects. Then, you could pass both these objects into a function that looks something like this.
function mergeSets(first, second) {
var result = first;
second.forEach(function (item, index, array) {
var resultIndex = contains(result, item);
if (resultIndex === -1) {
result.push(item);
} else {
result[resultIndex].numPages = item.numPages;
}
});
return result;
}
Notice that mergeSets() calls contains() which is essentially as follows.
function contains(set, object) {
var solution = -1;
set.forEach(function (item, index, array) {
if (item.bookTitle == object.bookTitle && item.author == object.author) {
solution = index;
}
});
return solution;
}
It really isn't too hard as you can see. Sorry for some of the variable names. This was written in a hurry. Also, you mention in your example of the resulting set that you would like the fields that aren't available to be displayed as null which is not appropriate since null usually indicates an empty reference. Instead, I have ignored them. Accessing those fields on objects in the array that don't have them would result in undefined which makes perfect sense.
Also, the following are the limitations of the code in the fiddle. You may edit it to ease out these limitations and make it more robust.
It is tied to the data format you have mentioned in your question. To make it work for arbitrary sets, you can check for the existence of a property using Object.hasOwnProperty() in a for-in loop and add the necessary ones resulting in a merge.
It doesn't handle duplicates within sets in anyway.
http://jsfiddle.net/x5Q5g/
Edit: Oh! And by the way, the code is JavaScript and the data format could be JSON provided you use JSON.parse() and JSON.stringify().
Edit: The following updates negate the first limitation mentioned above. Notice that you need to pass in the key to compare on the basis of, explicitly.
function contains(set, object, key) {
var solution = -1;
set.forEach(function (item, index, array) {
if (item[key] === object[key]) {
solution = index;
}
});
return solution;
}
function mergeSets(first, second, key) {
var result = first;
second.forEach(function (item, index, array) {
var resultIndex = contains(result, item, key);
if (resultIndex === -1) {
result.push(item);
} else {
result[resultIndex].numPages = item.numPages;
for (var property in item) {
if (item.hasOwnProperty(property)) {
if (!result[resultIndex].hasOwnProperty(property)) {
result[resultIndex].property = item.property;
}
}
}
}
});
return result;
}
var solution = mergeSets(firstSet, secondSet, "bookTitle");
console.log(solution);
http://jsfiddle.net/s6HqL/
One final update: Here is how you can make it accept any number of keys. I had forgotten that you require multiple key support. Sorry!
You need to change the following.
function contains(set, object, keys) {
var solution = -1;
set.forEach(function (item, index, array) {
var selfItem = item;
var allKeys = keys.every(function (item, index, array) {
if (selfItem[item] === object[item]) {
return true;
}
});
if (allKeys) {
solution = index;
}
});
return solution;
}
function mergeSets(first, second) {
var result = first;
var keys = Array.prototype.slice.call(arguments, 2);
second.forEach(function (item, index, array) {
var resultIndex = contains(result, item, keys);
if (resultIndex === -1) {
result.push(item);
} else {
for (var property in item) {
if (item.hasOwnProperty(property)) {
if (!result[resultIndex].hasOwnProperty(property)) {
var hello = result[resultIndex];
hello[property] = item[property];
}
}
}
}
});
return result;
}
var solution = mergeSets(firstSet, secondSet, "bookTitle", "author");
console.log(solution);
http://jsfiddle.net/s6HqL/3/
This last fiddle and code above it is complete. Without any references! And is generic. Would work with any number of keys as arguments.