Remove duplicate objects from array by a certain merge algorithm - javascript

Here's what I have in mind:
Given an array of objects:
[
{
"name": "Kirk",
"count": 1
},
{
"name": "Spock",
"count": 1
},
{
"name": "Kirk",
"count": 1
}
]
I am trying to get:
[
{
"name": "Kirk",
"count": 2
},
{
"name": "Spock",
"count": 1
}
]
I am wondering if there's already an algorithm, perhaps combining some higher order functions to achieve this. I could do this easily with loops, but I am looking for a way to solve it using higher order functions. If someone could point me to what I should use to achieve this, it would be great. Again, I'm looking for something as elegant as possible (two maps and a filter would not be a big improvement from loops).
This is my current solution and I'm looking for something better (and by better I mean more expressive):
function mergeDuplicates(input) {
var output = [];
var existingItem = null;
input.forEach(function (inputItem) {
existingItem = _.find(output, function (outputItem) {
return inputItem.name === outputItem.name;
});
existingItem ? existingItem.count += 1 : output.push({
name: inputItem.name,
count: 1
});
existingItem = null;
});
return output;
}
To make line #10 more clear: in the original array, count might be either non-existing or 1, hence I set it to 1.

I think the best way would be to hash each object if it does not already exist, and delete the ones that you found already hashed in your structure. This way, you'd be checking the existence of each object only 1 (depends on your hash scheme).

Just a function if you would like to use.
function merge(arr) {
for(var o = {}, i; i=arr.shift(); o[i.name] = i.count + (o[i.name] || 0));
for(i in o) arr.push({name:i, count:o[i]});
}
Calling :
var myArray = [{"name":"Kirk","count":1},
{"name":"Spock","count":1},
{"name":"Kirk","count":1}];
merge(myArray);
// myArray is now : [{"name":"Kirk","count":2}, {"name":"Spock","count":1}]

You can use reduce which is actually a fold.
a.reduce(function(p, c) {
var n = c.name;
if (p[n])
p[n].count++;
else
p[n] = c;
return p;
}, {})
will give you a object with "Kirk" and "Spock" as the key, what you want as values.

I know this is an old question, but I couldn't resist trying to solve it. Instead of two maps and a filter, we use a sort and then a reduce. This was a fun one to sort out :-)
function mergeDuplicates(list, prop, cb){
return list.sort(function(a,b){
if(a[prop] < b[prop]){ return -1;}
if(a[prop] > b[prop]){return 1;}
return 0;
}).reduce(function(acc, item, index, array){
if(index > 0 && array[index-1][prop] === item[prop]){
cb(acc[acc.length-1], item);
return acc;
}else{
var newItem = Object.assign({}, item);
cb(newItem);
acc.push(newItem);
return acc;
}
}, []);
}
Then use it like this:
var newList = mergeDuplicates(list, "name", function(item, dup){
if(dup){
item.count++;
}else{
item.count = 1;
}
});
EDIT: Here's another take at it using reduce and using an object as a hashmap to store duplicates (similar to some of the other answers). This one uses ramdajs
const mergeDups = (cb, prop, list) => R.pipe(
R.reduce((acc, item) => (
R.has(item[prop], acc) ?
R.assoc(item[prop], cb(acc[item[prop]], item), acc) :
R.assoc(item[prop], cb(item), acc)
), {}),
R.values
)(list);
const cb = (i, d) => ( !R.isNil(d) ?
R.assoc('count', i.count + 1, i) :
R.assoc('count', 1, i) )
mergeDups(cb, 'name', items);
Here it is in the repl on Ramda's site

Try this better i'll useful resolve your issues
cleanup(arrayOfObj, 'name');
function cleanup(arr, prop) {
var new_arr = [];
var lookup = {};
for (var i in arr) {
lookup[arr[i][prop]] = arr[i];
}
for (i in lookup) {
new_arr.push(lookup[i]);
}
return new_arr;
}

Yet another version using reduce function:
var items =
[
{
"name": "Kirk",
"count": 1
},
{
"name": "Spock",
"count": 1
},
{
"name": "Kirk",
"count": 1
}
];
var filtered = items.reduce(function(prev, current,index){
if(!(current.name in prev.keys)) {
prev.keys[current.name] = index;
prev.result.push(current);
}
else{
prev.result[prev.keys[current.name]].count += current.count;
}
return prev;
},{result: [], keys: []}).result;
document.getElementById("output").innerHTML = JSON.stringify(filtered,null,2);
<pre id='output' />

Related

How to flatten/reduce only the deepest or otherwise specicified level of an Nth-dimensional array

assuming an N-th dimensional array, eg.
const array = [
"1",
["2","2"],
[["3","3"],["3","3"]],
[
[
[["4","4"],"3"],
[["4","4"],"3"]
],
],
[["3","3"],["3","3"]],
["2","2"],
"1"
];
I can only find methods to flatten arrays from shallow-end of the index up, but I need to flatten/reduce or otherwise handle only the deepest (or any arbitrary) index level.
When running the deepest level I am looking for an array output something along the lines of
array = [
"1",
["2","2"],
[["3","3"],["3","3"]],
[
[
["4,4","3"],
["4,4","3"]
],
],
[["3","3"],["3","3"]],
["2","2"],
"1"
];
I cannot find a solution that isn't garbage... (over-complicated/incredibly messy)
Any help would be appreciated
You could create a function that will take the data and level that you want to flatten and will flatten only that level. Then to get the last level you can create another function.
const array = ["1",["2","2"],[["3","3"],["3","3"]],[[[["4","4"],"3"],[["4","4"],"3"]],[["",""],["",""]]],[["3","3"],["3","3"]],["2","2"],"1"];
function lastLvl(data, lvl = 0) {
return data.reduce((r, e) => {
if (lvl > r) r = lvl
if (Array.isArray(e)) {
const nlvl = lastLvl(e, lvl + 1);
if (nlvl > r) r = nlvl
}
return r
}, 0)
}
function flattenLvl(data, lvl, clvl = 1) {
return data.reduce((r, e) => {
if (Array.isArray(e)) {
const nested = flattenLvl(e, lvl, clvl + 1);
if (clvl == lvl) r.push(...nested);
else r.push(nested)
} else {
r.push(e)
}
return r;
}, [])
}
const lvl = lastLvl(array)
const result = flattenLvl(array, lvl)
console.log(result)
You can use a recursive function that gets the array and a desired level, and then stop recurring when the level is 1, at which point you just filter out the non-array values in that (sub)array:
function extractLevel(arr, level) {
return level <= 1
? arr.filter(val => !Array.isArray(val))
: arr.filter(Array.isArray)
.flatMap(arr => extractLevel(arr, level-1));
}
// demo
const array = ["1",["2","2"],[["3","3"],["3","3"]],[[[["4","4"],"3"],[["4","4"],"3"]],[["",""],["",""]]],[["3","3"],["3","3"]],["2","2"],"1"];
// extract the values (not arrays) at level 3:
console.log(extractLevel(array, 3));

lodash / js: Filtering values within an object based on regular expressions and getting the highest by comparison

For the following json
[
{
"index": "xyz",
...
},
{
"index": "abc1234",
...
},
{
"index": "xyz",
...
},
{
"index": "abc5678",
...
}
...
I want to filter out abc values and xyz values separately.
I tried the following to get values
var x = _.filter(jsonData, function (o) {
return /abc/i.test(o.index);
});
and it worked to give the filtered outputs.
Now i want to get the highest of abc values that is if there values abc123, abc444, abc999 then the code should return abc999.
I can loop over again using lodash but could this be done in a single call - within the same one that filters out?
You can use Array.prototype.reduce(), String.prototype.replace() with RegExp /\D+/ to match and remove characters that are not digits. Check if previous number portion of string is less than current number portion of string
var jsonData = [
{
"index": "xyz",
},
{
"index": "abc1234",
},
{
"index": "xyz",
},
{
"index": "abc5678",
},
{
"index": "abc1",
}];
var x = jsonData.reduce(function (o, prop) {
return /abc/i.test(prop.index)
? !o || +prop.index.replace(/\D+/, "") > +o.replace(/\D+/, "")
? prop.index
: o
: o
}, 0);
console.log(x);
Following is a crude and unsatisfactory implementation:
//filter out matching objects for possible future use
var latest = "";
var matches = _.filter(jsonData, function (o) {
var ret = /abc/i.test(o.index);
if (ret) {
var digits = o.index.replace(/\D/g,'')
if (parseInt(digits) > latest) {
latest = digits;
latestIndex = o.index
console.log(latest+">>>latestIndex")
}
return true;
}
return false;
});
console.log("latestIndex->"+latest);
}
If you want to find the highest abc{SOME_NUMBER} value and filter at the same time, you can just use regular iteration instead of _.filter:
let jsonData = [{"index": "xyz"},{"index": "abc1234"}, {"index": "xyz"},{"index": "abc5678"}];
let max = Number.MIN_SAFE_INTEGER; // value for the max number at the end of "abc"
let item; // item containing the max abc${NUMBER} value
let filtered = []; // filtered array containing abc strings
jsonData.forEach((curr) => {
// filter test
if (/abc/i.test(curr.index)) {
filtered.push(curr);
// max value test
const [digits] = curr.index.match(/\d+/);
const test = parseInt(digits);
if (test > max) {
max = test;
item = curr;
}
}
});
console.log('Item:\n', item, '\n\n----\nFiltered:\n', filtered);
One way to do it using lodash is by replacing filter with maxBy in your code.
Of course, this has the downside that if no valid elements exist in the collection, it'll arbitrarily return an invalid one. So, after getting the result, an extra validity check is needed.
This is why I have extracted the validation/filter code in a separate function:
var jsonData = [{
"index": "xyz",
}, {
"index": "abc1234",
}, {
"index": "xyz",
}, {
"index": "abc5678",
}];
var isValid = function(o) {
return /abc/i.test(o.index);
};
var highest = _.maxBy(jsonData, isValid);
if (isValid(highest)) {
console.log('The max value is: ' + highest.index);
} else {
console.log('No valid value found!');
}
<script src="https://cdn.jsdelivr.net/lodash/4/lodash.min.js"></script>
And here's how it works if there are no valid elements:
var jsonDataWithoutValidValues = [{
"index": "xyz",
}, {
"index": "xyz",
}];
var isValid = function(o) {
return /abc/i.test(o.index);
};
var highest = _.maxBy(jsonDataWithoutValidValues , isValid);
if (isValid(highest)) {
console.log('The max value is: ' + highest.index);
} else {
console.log('No valid value found!');
}
<script src="https://cdn.jsdelivr.net/lodash/4/lodash.min.js"></script>
This is a probably a bit weird to use in production, but I thought it was interesting enough to share.
Well in this case you can just use Array prototype sort after you have filtered out the 'abc' to sort them the way you want
var x = _.filter(jsonData, function (o) {
return /abc/i.test(o.index);
}).sort(function (a, b) {
if(a.index > b.index) return -1;
if(a.index < b.index) return 1;
return 0;
});
if you do the sorting correct you can get the highest value like
console.log(x[0].index)
You could use a single loop with Array#reduce and check the number, if exists.
var data = [{ index: "xyz" }, { index: "abc1234" }, { index: "xyz" }, { index: "abc5678" }],
getNumber = function (s) { return s.match(/^abc(\d+)/i)[1]; },
result = data.reduce(function (r, a) {
return a.index.match(/^abc\d/i) && (!r || getNumber(r.index) < getNumber(a.index)) ? a : r;
}, undefined);
console.log(result);
Here's a lodash chaining approach:
_(data)
.map('index')
.filter(_.method('match', /abc/))
.maxBy(_.flow(_.bindKey(/\d+/, 'exec'), _.first, _.toNumber));
The map() and filter() calls get you a list of stings with abc in them. The maxBy() call finds the max, but we have to compose a function to tell it that we want to compare it numerically. The flow() function is really handy for this. Here, we're telling it to execute the regular expression, find the first element of the result, and turn that into a number.

Remove duplicate objects from an array using javascript

I am trying to figure out an efficient way to remove objects that are duplicates from an array and looking for the most efficient answer. I looked around the internet everything seems to be using primitive data... or not scalable for large arrays. This is my current implementation which is can be improved and want to try to avoid labels.
Test.prototype.unique = function (arr, artist, title, cb) {
console.log(arr.length);
var n, y, x, i, r;
r = [];
o: for (i = 0, n = arr.length; i < n; i++) {
for (x = 0, y = r.length; x < y; x++) {
if (r[x].artist == arr[i].artist && r[x].title == arr[i].title) {
continue o;
}
}
r.push(arr[i]);
}
cb(r);
};
and the array looks something like this:
[{title: sky, artist: jon}, {title: rain, artist: Paul}, ....]
Order does not matter, but if sorting makes it more efficient then I am up for the challenge...
and for people who do not know o is a label and it is just saying jump back to the loop instead of pushing to the new array.
Pure javascript please no libs.
ANSWERS SO FAR:
The Performance Test for the answers below:
http://jsperf.com/remove-duplicates-for-loops
I see, the problem there is that the complexity is squared. There is one trick to do it, it's simply by using "Associative arrays".
You can get the array, loop over it, and add the value of the array as a key to the associative array. Since it doesn't allow duplicated keys, you will automatically get rid of the duplicates.
Since you are looking for title and artist when comparing, you can actually try to use something like:
var arrResult = {};
for (i = 0, n = arr.length; i < n; i++) {
var item = arr[i];
arrResult[ item.title + " - " + item.artist ] = item;
}
Then you just loop the arrResult again, and recreate the array.
var i = 0;
var nonDuplicatedArray = [];
for(var item in arrResult) {
nonDuplicatedArray[i++] = arrResult[item];
}
Updated to include Paul's comment. Thanks!
Here is a solution that works for me.
Helper functions:
// sorts an array of objects according to one field
// call like this: sortObjArray(myArray, "name" );
// it will modify the input array
sortObjArray = function(arr, field) {
arr.sort(
function compare(a,b) {
if (a[field] < b[field])
return -1;
if (a[field] > b[field])
return 1;
return 0;
}
);
}
// call like this: uniqueDishes = removeDuplicatesFromObjArray(dishes, "dishName");
// it will NOT modify the input array
// input array MUST be sorted by the same field (asc or desc doesn't matter)
removeDuplicatesFromObjArray = function(arr, field) {
var u = [];
arr.reduce(function (a, b) {
if (a[field] !== b[field]) u.push(b);
return b;
}, []);
return u;
}
and then simply call:
sortObjArray(dishes, "name");
dishes = removeDuplicatesFromObjArray(dishes, "name");
Basic sort-then-unique implementation, fiddle HERE:
function unique(arr) {
var comparer = function compareObject(a, b) {
if (a.title == b.title) {
if (a.artist < b.artist) {
return -1;
} else if (a.artist > b.artist) {
return 1;
} else {
return 0;
}
} else {
if (a.title < b.title) {
return -1;
} else {
return 1;
}
}
}
arr.sort(comparer);
console.log("Sorted: " + JSON.stringify(arr));
for (var i = 0; i < arr.length - 1; ++i) {
if (comparer(arr[i], arr[i+1]) === 0) {
arr.splice(i, 1);
console.log("Splicing: " + JSON.stringify(arr));
}
}
return arr;
}
It may or may not be the most efficient, and should be entirely scalable. I've added some console.logs so you can see it as it works.
EDIT
In the interest of saving on the space the function used, I did that for loop at the end, but it seems likely that didn't properly find only unique results (depsite it passing my simple jsfiddle test). Please try replacing my for loop with the following:
var checker;
var uniqueResults = [];
for (var i = 0; i < arr.length; ++i) {
if (!checker || comparer(checker, arr[i]) != 0) {
checker = arr[i];
uniqueResults.push(checker);
}
}
return uniqueResults;
I use this function. its not doing any sorting, but produces result. Cant say about performance as never measure it.
var unique = function(a){
var seen = [], result = [];
for(var len = a.length, i = len-1; i >= 0; i--){
if(!seen[a[i]]){
seen[a[i]] = true;
result.push(a[i]);
}
}
return result;
}
var ar = [1,2,3,1,1,1,1,1,"", "","","", "a", "b"];
console.log(unique(ar));// this will produce [1,2,3,"", "a", "b"] all unique elements.
Below is Henrique Feijo's answer with ample explanation and an example that you can cut and paste:
Goal: Convert an array of objects that contains duplicate objects (like this one)...
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
... Into an array of objects without duplicate objects (like this one):
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
Explanation provided in the comments:
var allContent = [{
"id": 10620,
"name": "Things to Print"
}, {
"id": 10620,
"name": "Things to Print"
}, {
"id": 4334,
"name": "Interesting"
}]
//Put Objects Into As Associative Array. Each key consists of a composite value generated by each set of values from the objects in allContent.
var noDupeObj = {} //Create an associative array. It will not accept duplicate keys.
for (i = 0, n = allContent.length; i < n; i++) {
var item = allContent[i]; //Store each object as a variable. This helps with clarity in the next line.
noDupeObj[item.id + "|" + item.name] = item; //This is the critical step.
//Here, you create an object within the associative array that has a key composed of the two values from the original object.
// Use a delimiter to not have foo+bar handled like fo+obar
//Since the associative array will not allow duplicate keys, and the keys are determined by the content, then all duplicate content are removed.
//The value assigned to each key is the original object which is along for the ride and used to reconstruct the list in the next step.
}
//Recontructs the list with only the unique objects left in the doDupeObj associative array
var i = 0;
var nonDuplicatedArray = [];
for (var item in noDupeObj) {
nonDuplicatedArray[i++] = noDupeObj[item]; //Populate the array with the values from the noDupeObj.
}
console.log(nonDuplicatedArray)
For those who love ES6 and short stuff, here it's one solution:
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" }
];
Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Jon" },
{ title: "cry", artist: "Jon" }
];
const unique = Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
console.log(`New array length: ${unique.length}`)
console.log(unique)
The above example only works for a unique title or id. Basically, it creates a new map for songs with duplicate titles.
Below code compares object with JSON as String format and removes duplicates and works fine with simple arrays.
Array.prototype.unique=function(a){
return function(){
return this.filter(a)
}
}(
function(a,b,c){
var tmp=[];
c.forEach(function(el){
tmp.push(JSON.stringify(el))
});
return tmp.indexOf(JSON.stringify(a),b+1)<0
})
If you are using underscore js, it is easy to remove duplicate object.
http://underscorejs.org/#uniq
function remove_duplicates(objectsArray) {
var arr = [], collection = [];
$.each(objectsArray, function (index, value) {
if ($.inArray(value.id, arr) == -1) {
arr.push(value.id);
collection.push(value);
}
});
return collection;
}

Sort javascript array by property or function value

I have an array that contains raw values as well as computed values. I would like to be able to sort the array dynamically based on either a raw value or the result of one of the computed values. The actual sorts that will be required will not be known until runtime.
I've put together the below sample (plunker here) that demonstrates the situation and a working solution*. I would like to know how to improve this... specifically, the use of:
Array.prototype.sortBy = function (property) {
return this.sort(mySort(property));
};
is copied from this stackoverflow response - and Ege Özcan specifically states
//Please don't just copy-paste this code.
//See the explanation at the end. A lot could break.
I would like to understand how to implement this sorting algorithm on my object without violating the 'A lot could break' warning (that I don't understand).
*One of the things I love about stackoverflow is that the process of framing the question well frequently leads you to simplify the problem to the point where a (not necessarily the) solution presents itself. I started this problem not being able to sort based on a property or computed value. Now, I'm looking for validation/improvement on the implementation.
Sample:
var rawData = [
{ "Id": 3, "itemCount": 3531, "val1": 905, "val2": 172 },
{ "Id": 2, "itemCount": 3111, "val1": 799, "val2": 147 },
{ "Id": 4, "itemCount": 3411, "val1": 871, "val2": 199 },
{ "Id": 5, "itemCount": 3414, "val1": 892, "val2": 178 },
{ "Id": 1, "itemCount": 3182, "val1": 845, "val2": 155 }
];
function MyItem(item) {
var self = this;
for (var val in item) {
if (item.hasOwnProperty(val)) {
self[val] = item[val];
}
}
}
function extendMyItems() {
MyItem.prototype.computedOne = function () {
var input = this;
return input.itemCount / input.val1;
};
MyItem.prototype.computedTwo = function () {
var input = this;
return input.val1 * input.val2;
};
}
function getItems(input) {
var ret = [];
for (var i = 0; i < input.length; i++) {
var item = new MyItem(input[i]);
ret.push(item);
}
return ret;
}
function doIt() {
Array.prototype.sortBy = function (property) {
return this.sort(mySort(property));
};
extendMyItems();
var sortList = [{ "sortKey": "Id", "sortOrder": "asc" },
{ "sortKey": "val1", "sortOrder": "asc" },
{ "sortKey": "val2", "sortOrder": "desc" },
{ "sortKey": "computedOne", "sortOrder": "desc", "isComputed": true },
{ "sortKey": "Id", "sortOrder": "desc" },
{ "sortKey": "computedTwo", "sortOrder": "asc", "isComputed": true }];
// get the array of MyItem
var myItems = getItems(rawData);
for (var k = 0; k < sortList.length; k++) {
myItems.sortBy(sortList[k]);
// process the sorted items here (ranking/scoring, etc)
for (var p = 0; p < myItems.length; p++) {
console.log('Id: ' + myItems[p].Id + ' val1: ' + myItems[p].val1 + ' val2: ' + myItems[p].val2 + ' c1: ' + myItems[p].computedOne() + ' c2: ' + myItems[p].computedTwo());
}
}
function mySort(srt) {
var so = srt.sortOrder == 'asc' ? 1 : -1;
var key = srt.sortKey;
var result = 0;
console.log(srt.sortKey + ' ' + srt.sortOrder + ':');
return function (a, b) {
if (srt.isComputed) {
// this seems like a hack - is there a better way to switch between property and function value????
result = (a[key]() < b[key]()) ? -1 : (a[key]() > b[key]()) ? 1 : 0;
} else {
result = (a[key] < b[key]) ? -1 : (a[key] > b[key]) ? 1 : 0;
}
return result * so;
};
}
}
It is considered by most a bad practice to extend native objects like Array. That is what the post you mentioned was getting at. The concern is you do not know how this will effect the behavior of other scripts.
Here is an example of a scenario where poorly written caused issues once the array prototype was manipulated. This is a scenario I ran into once on a large code based which was really hard to track down:
function BadUseOfForLoop(){
//You should NEVER use a for in loop to iterate over an array
//although some people do this and it works until you extend Array
var arr = [1,2,3,4];
for (key in arr){
console.log(arr[key]);
}
}
BadUseOfForLoop();
console.log("Extend Array...");
Array.prototype.sortBy = function(){
return "Doesnt matter...";
};
BadUseOfForLoop();
Output:
1
2
3
4
Extend Array...
1
2
3
4
function (){
return "Doesnt matter...";
}
http://jsfiddle.net/vTwRY/
One thing you could do to avoid the caveat is simply not extend the Array object and create a helper to do this for you.
var ArrayHelper = {
sortBy : function(arr, prop){
return function(){
var so = srt.sortOrder == 'asc' ? 1 : -1;
var key = srt.sortKey;
var result = 0;
console.log(srt.sortKey + ' ' + srt.sortOrder + ':');
return function (a, b) {
if (srt.isComputed) {
result = (a[key]() < b[key]()) ? -1 : (a[key]() > b[key]()) ? 1 : 0;
} else {
result = (a[key] < b[key]) ? -1 : (a[key] > b[key]) ? 1 : 0;
}
return result * so;
};
}
}
};
And then in your code...
ArrayHelper.sortBy(myItems,sortList[k]);
instead of
myItems.sortBy(sortList[k]);
Working demo
For further reading on the subject this Perfection Kills post discusses whether or not extending native objects is a good idea and you will see it is not a cut and dry issue. Above, I layed out an issue that is not discussed in this blog post but really can cause conflicts with other code.
The answer states that you shouldn't extend things like Array.prototype. Personally, I don't consider this a mortal sin; most libraries can safely handle these scenarios.
That said, you could wrap the sorting functionality:
function ArraySorter(arr)
{
this.arr = arr;
}
ArraySorter.prototype.sortBy = function(prop) {
return this.arr.sort(mySort(prop));
}
var sortedItems = new ArraySorter(myItems).sortBy('whatever');

Count duplicates within an Array of Objects

I have an array of objects as follows within my server side JS:
[
{
"Company": "IBM"
},
{
"Person": "ACORD LOMA"
},
{
"Company": "IBM"
},
{
"Company": "MSFT"
},
{
"Place": "New York"
}
]
I need to iterate through this structure, detect any duplicates and then create a count of a duplicate is found along side each value.
Both of the values must match to qualify as a duplicate e.g. "Company": "IBM" is not a match for "Company": "MSFT".
I have the options of changing the inbound array of objects if needed. I would like the output to be an object, but am really struggling to get this to work.
EDIT: Here is the code I have so far where processArray is the array as listed above.
var returnObj = {};
for(var x=0; x < processArray.length; x++){
//Check if we already have the array item as a key in the return obj
returnObj[processArray[x]] = returnObj[processArray[x]] || processArray[x].toString();
// Setup the count field
returnObj[processArray[x]].count = returnObj[processArray[x]].count || 1;
// Increment the count
returnObj[processArray[x]].count = returnObj[processArray[x]].count + 1;
}
console.log('====================' + JSON.stringify(returnObj));
For example:
counter = {}
yourArray.forEach(function(obj) {
var key = JSON.stringify(obj)
counter[key] = (counter[key] || 0) + 1
})
Docs: Array.forEach, JSON.stringify.
Object.prototype.equals = function(o){
for(var key in o)
if(o.hasOwnProperty(key) && this.hasOwnProperty(key))
if(this[key] != o[key])
return false;
return true;
}
var array = [/*initial array*/],
newArray = [],
ok = true;
for(var i=0,l=array.length-1;i<l;i++)
for(var j=i;j<l+1;j++)
{
if(!array[i].equals(array[j]))
newArray.push(array[i]);
}
We are required to write a JavaScript function that takes in one such array of objects. The function creates and return a new array in which no objects are repeated (by repeated we mean objects having same value for "Country" property.)
Moreover, the function should assign a count property to each object that represents the number of times they appeared in the original array.
const arr = [
{
"Country": "BR",
"New Lv1−Lv2": "#N/A"
},
{
"Country": "BR",
"New Lv1−Lv2": "#N/A"
},
{
"Country": "",
"New Lv1−Lv2": "test"
}];
const convert = (arr) => {
const res = {};
arr.forEach((obj) => {
const key = `${obj.Country}${obj["New Lv1−Lv2"]}`;
if (!res[key]) {
res[key] = { ...obj, count: 0 };
};
res[key].count += 1;
});
return Object.values(res);
};
console.log(convert(arr));
know more
With ES6, one can use Array#reduce with an object to store the counts.
let counts = arr.reduce((acc, curr)=>{
const str = JSON.stringify(curr);
acc[str] = (acc[str] || 0) + 1;
return acc;
}, {});
Demo
To create a new array without duplicates, a Set can be used with Array#filter.
let set = new Set;
let res = arr.filter(x => {
const str = JSON.stringify(x);
return !set.has(str) && set.add(str);
});
Demo

Categories