Using jQuery - I would like to compare 2 JSON arrays:
origArray comes from the database/C# API:
var origArray = [ {
"TypeName": "Single",
"TypeID": "3121",
"TypeCount": "2"
},
{
"TypeName": "Double",
"TypeID": "4056",
"TypeCount": "2"
},
{
"TypeName": "Family",
"TypeID": "5654",
"TypeCount": "4"
}
];
userArray is gathered from user input:
var userArray = [ {
"TypeID": "3121",
"TypeCount": "2"
},
{
"TypeID": "4056",
"TypeCount": "3"
},
{
"TypeID": "3121",
"TypeCount": "3"
}
];
What I would like to do, is loop through the userArray, and "group" by the TypeID, and Sum the TypeCount.
So in the example above:
TypeID: 3121
TypeCount: 5 (ie. 2 + 3)
TypeID: 4056
TypeCount: 3
Meaning there would be a difference of 3 over for TypeID 3121 and 1 over for 4056.
Is there any way of getting that information out of either jQuery or native Javascript (that would work cross-browser)?
Thanks for any help,
Mark
Define a function to group by each array by TypeID and sum TypeCount:
function groupByTypeID (arr) {
var groupBy = {};
$.each(arr, function () {
var currentCount = groupBy[this.TypeID] || 0;
groupBy[this.TypeID] = currentCount + parseInt(this.TypeCount);
});
return groupBy;
}
Then, group both arrays, and take their differences:
var userArrayGroups = groupByTypeID(userArray);
var origArrayGroups = groupByTypeID(origArray);
var diff = {};
for (var prop in userArrayGroups) {
diff[prop] = userArrayGroups[prop] - origArrayGroups[prop];
}
diff will then hold the following:
{
3121: 3
4056: 1
}
DEMO.
If you are familiar with C# this js library - linqjs.
It contains implementations of all .NET 4.0 LINQ methods and many extra methods.
You can do it with Underscore:
var result = _.chain(userArray)
.groupBy(function(value) {
// group by the TypeId
return value.TypeID;
})
.map(function(value) {
// iterate over every group
var addition = _.chain(value)
.pluck("TypeCount") // create an array with TypeCount values
.reduce(function(memo, num) {
return Number(memo) + Number(num); // add all the TypeCount values
})
.value();
return {
"TypeID": value[0].TypeID, // TypeID
"TypeCount": addition // total addition for this TypeID
};
})
.value();
Working example here: http://livecoding.io/3498441
I'll have a shot at it...
var mergeArray = []
var index;
for (var i = userArray.length - 1; i >= 0; i--) {
index = findByTypeID(mergeArray, userArray[i].TypeID);
if(!index){
mergeArray[index].TypeCount += parseInt(userArray[i].TypeCount)
} else{
mergeArray.push({
"TypeID": userArray[i].TypeID,
"TypeCount": parseInt(userArray[i].TypeCount)
});
}
};
function findByTypeID(arr, id){
for (var i = arr.length - 1; i >= 0; i--) {
if(arr[i].TypeID == id)
return i;
};
return -1;
}
This gives you your desired data structure output inside mergeArray
result = {};
for (var i in userArray) {
var elem = userArray[i]
if (!result[elem.TypeID])
result[elem.TypeID] = elem.TypeCount;
else
result[elem.TypeID] += elem.TypeCount;
}
return result;
Related
How to convert a string to JSON with javascript or jQuery? I've been thinking all day, but I do not get a good idea.
This task is to dynamically create the treeview in the client side (ASP.Net). My idea is to convert the string to an object and convert to JSON type. (String -> object -> JSON) I tried, but the day is gone. It is difficult to construct 2 more depth like A->a3->a31.
String is
var sString = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
and JSON format is
{
"title": "A",
"key": "1",
"folder": true,
"children": [{
"title": "a1",
"key": "2"
}, {
"title": "a2",
"key": "3"
}, {
"title": "a3",
"key": "4",
"folder": true,
"children": [{
"title": "a31",
"key": "5"
}...
}]
}
(This is fancytreeview plugin)
‘//‘ is depth and ‘,’ is split.
Please help me..
Edit)
I want to turn ‘sString’ to JSON format.. but It’s ok just JSON type string.
Please understand that my sentence is strange because my native language is not English.
Edit2)
oh.. I want to convert the string to an object and then convert it back to JSON format. I do not have the confidence to convert that string into JSON format right away. Because there are more than 8000 variants. If It’s can, let me know how.
I believe this can be done without recursion:
var string = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
// Take all the roots
var roots = string.split(',');
// We will attach it to every node and keep it incrementing
var key = 1;
// The final result will be in this object
var result = [];
// Loop through to found roots
roots.forEach(function(root) {
// Take all the children
var items = root.split('//');
var parent = result;
// Loop through the available children
items.forEach(function(item, i) {
// Find if the current item exists in the tree
var child = getChild(parent, item);
if (!child) {
child = {
title: item,
key: key++
}
// This will ensure that the current node is a folder only
// if there are more children
if (i < items.length - 1) {
child.folder = true;
child.children = [];
}
// Attach this node to parent
parent.push(child);
}
parent = child.children;
});
});
console.log(result);
// Utility function to find a node in a collection of nodes by title
function getChild(parent, title) {
for (var i = 0; i < parent.length; i++) {
if (parent[i].title === title) {
return parent[i];
}
}
}
This is the draft code which came in my mind at first. I believe it can be improved further in terms of complexity.
var key = 1; // keys start at 1
let addPaths = (root, paths) => {
if (!paths || paths.length == 0)
return;
let path = paths.shift();
//add nodes for the current path
addNodes(root, path.split('//'));
// keep going until all paths have been processed
addPaths(root, paths);
};
let addNodes = (root, nodeList) => {
if (!nodeList || nodeList.length == 0)
return;
let title = nodeList.shift();
// find node under root with matching title
let isRootNode = Array.isArray(root);
node = (isRootNode ? root : root.children || []).find((node) => {
return node.title == title;
});
if (!node){
node = {
title: title,
key: key++
}
// are we at root of object?
if (isRootNode)
root.push(node);
else
{
if (!root.children)
root.children = [];
root.children.push(node);
root.folder = true;
}
}
addNodes(node, nodeList);
};
let parse = (string) => {
let object = [];
let nodes = string.split(',');
addPaths(object, nodes);
return object
};
console.log(JSON.stringify(parse("A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2"), null, 2));
Which results in:
[
{
"title": "A",
"key": 1,
"children": [
{
"title": "a1",
"key": 2
},
{
"title": "a2",
"key": 3
},
{
"title": "a3",
"key": 4,
"children": [
{
"title": "a31",
"key": 5
},
{
"title": "a32",
"key": 6
}
],
"folder": true
}
],
"folder": true
},
{
"title": "B",
"key": 7
},
{
"title": "C",
"key": 8,
"children": [
{
"title": "c1",
"key": 9
},
{
"title": "c2",
"key": 10
}
],
"folder": true
}
]
Try below code. I have used associative array to store already processed folder for faster lookup.
I hope it helps you.
var sString = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
var sArr = sString.split(","); // We will split it by comma so that we can iterate through its items.
var output = []; // Final result will be stored here.
var hash = {}; // It used to keep track of itemObjectect's position for faster lookup.
var counter = 1; // Its value will be used to assign to key;
for(var i = 0; i < sArr.length; i++){
var items = sArr[i].split("//");
var itemObject = {}; // Object to store value of each item.
var parentItemObject = {}; // It will refer to current parentObject during iteration.
for(var j = 0; j < items.length; j++){
// Check if item is already processed and stored in hash map.
if(hash.hasOwnProperty(items[j])){
// Check if parent Object value is empty then we will fetch it from hash directly.
if(isEmpty(parentItemObject)){
parentItemObject = output[hash[items[j]]];
}
else{
// It is parent element but is child of another element. Then we will fetch it from it's children array.
if(typeof parentItemObject.children !== "undefined"){
parentItemObject = parentItemObject.children[hash[items[j]]];
}
}
continue;
}
itemObject.title = items[j];
itemObject.key = counter++;
// Check if it is a folder item.
if(j != items.length -1){
itemObject.folder = true;
itemObject.children = [];
if(isEmpty(parentItemObject)){
parentItemObject = itemObject;
hash[itemObject.title] = output.length;
output.push(itemObject);
}
else{
if(typeof parentItemObject.children !== "undefined"){
hash[itemObject.title] = parentItemObject.children.length;
parentItemObject.children.push(itemObject);
}
parentItemObject = itemObject;
}
}
else{
if(isEmpty(parentItemObject)){
parentItemObject = itemObject;
hash[itemObject.title] = output.length;
output.push(itemObject);
}
if(typeof parentItemObject.children !== "undefined"){
hash[itemObject.title] = parentItemObject.children.length;
parentItemObject.children.push(itemObject);
}
}
itemObject = {};
}
//console.log(items);
}
function isEmpty(itemObject) {
return Object.keys(itemObject).length === 0;
}
//console.log(hash);
console.log(JSON.stringify(output,null,2));
If I have an array like this:
var array1 =
[
{"phraseId":"abc",
"keyword":"bb",
"posId":1},
{"phraseId":"def",
"keyword":"bb",
"posId":1},
]
How can I find out that the object with phraseId of "def" has the 2nd position?
You could map your object and only return the target field, and then use the built in indexOf to get the position:
array1.map(item => item.phraseId).indexOf('def')
Use native JavaScript findIndex method.
var array1 = [{
"phraseId": "abc",
"keyword": "bb",
"posId": 1
}, {
"phraseId": "def",
"keyword": "bb",
"posId": 1
}, ];
var pos = array1.findIndex(function(v) {
// set your condition for finding object
return v.phraseId == 'def';
// add `1` since you want to count from `1`
}) + 1;
console.log("Position of the object " + pos);
For older browser check polyfill option.
With ES6 arrow function
var array1 = [{
"phraseId": "abc",
"keyword": "bb",
"posId": 1
}, {
"phraseId": "def",
"keyword": "bb",
"posId": 1
}, ];
var pos = array1.findIndex(v => v.phraseId == 'def') + 1;
console.log("Position of the object " + pos);
It works this way :
array1.forEach((elem, index) => {if (elem.phraseId === "def")
console.log("index = " + index);
});
Assuming that your key is know (that you know you are looking for a phraseId always) then you can simply iterate through the array with a normal for loop if you are using "traditional" JS, or with a forEach if you are using ES6. Here's the simple for implementation.
for (var i = 0; i < array1.length; i++ ){
if(array[i].phraseId === 'def') {
// we know "i" is the index, so do something...
}
}
To make it more generic so you can search any array for any key, make a function of it that returns the index:
function whatIndex (arr, key, val) {
for (var i = 0; i < arr.length; i++) {
if( arr[i][key] === val ) {
return i;
}
}
}
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;
}
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' />
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