Remove duplicate record by keeping one in object - javascript
I have remove duplicate function this will remove duplicate record. But I want atleast one copy of that.
Ex:
var myArr = [{"Country":"China","Rank":"2"},{"Country":"USA","Rank":"2"},{"Country":"China","Rank":"2"}];
O/P = [{"Country":"China","Rank":"2"},{"Country":"USA","Rank":"2"}]
I am deleting on basis of "Country".
My Code
removeDuplicates : function(myArr, Country) {
var finalArray = [];
var values = [];
var value;
for (var i = 0; i < myArr.length; i++) {
value = myArr[i][Country];
if (values.indexOf(value) === -1) {
finalArray.push(myArr[i]);
values.push(value);
}
}
return finalArray;
},
How to maintain original record and remove only duplicate.
Try something like this
var result=[];
myArr.map(function(x){
if(myArr.filter(y=>y.Country==x.Country).length==1 || (result.filter(r=>r.Country==x.Country).length==0))
result.push(x);
})
console.log(result);
I would use reduce & find here.
const myArr = [{"Country":"China","Rank":"2"},{"Country":"USA","Rank":"2"},{"Country":"China","Rank":"2"}];
const newArr = myArr.reduce((prev, cur) => {
if (prev.find((i) => i.Country === cur.Country)) return prev;
prev.push(cur);
return prev;
}, []);
Related
Delete all occurrence of duplicate values in an array in javascript/jquery
I am trying to work on a problem where I want to remove all the occurrences of similar value in an array eg. var sampleArr = ["mary","jane","spiderman","jane","peter"]; and I am trying to get result as => ["marry","spiderman","peter"] how do I get this?
You can use filter() var arr = ["mary","jane","spiderman","jane","peter"]; var unique = arr.filter((x, i) => arr.indexOf(x) === i); console.log(unique);
var sampleArr = ["mary","jane","spiderman","jane","peter"]; var unique = []; var itemCount = {}; sampleArr.forEach((item,index)=>{ if(!itemCount[item]){ itemCount[item] = 1; } else{ itemCount[item]++; } }); for(var prop in itemCount){ if(itemCount[prop]==1){ unique.push(prop); } } console.log(unique); Check this.
You can count the frequency of the character and just pick the character whose frequency is 1. const arr = ["mary","jane","spiderman","jane","peter"]; frequency = arr.reduce((o,ch) => { o[ch] = (o[ch] || 0) + 1; return o; }, {}), unique = Object.keys(frequency).reduce((r,k) => frequency[k] === 1? [...r, k]: r, []); console.log(unique);
You can use filter: var sampleArr = ["mary","jane","spiderman","jane","peter"]; var result = sampleArr.filter((e,_,s)=>s.filter(o=>o==e).length<2); // or with reduce and flatmap const result1 = Object.entries(sampleArr.reduce((a,e)=>(a[e]??=0, a[e]++, a),{})).flatMap(([k,v])=>v==1 ? k: []); console.log(result) console.log(result1);
lot of solution, here easy solution to understand using match to have the occurence and filter to eliminate: var arr = ['ab','pq','mn','ab','mn','ab']; var st = arr.join(","); result = arr.filter(it => { let reg = new RegExp(it, 'g'); return st.match(reg).length == 1; }); console.log(result);// here ["pq"]
filter seems to be your best bet here if you need extremely robust performance. No real need for jQuery in this instance. Personally, I would opt for readability for something like this and instead sort, set duplicates to null, and then remove all null values. var sampleArr = ["mary","jane","spiderman","jane","peter"]; var result = sampleArr.sort().forEach((value, index, arr) => { // if the next one is the same value, // set this one to null if(value === arr[value + 1]) return arr[index] = null; // if the previous one is null, and the next one is different, // this is the last duplicate in a series, so should be set to null if(arr[value - 1] === arr[index + 1] !== value) return arr[index] = null; return }) .filter(value => value === null) //remove all null values console.log(result);
Delete the previous and next element from an array while using array.map() in javascript
How can I delete the previous and next element from an array while using array.map(); In the code below, when I get to 'crossover' I want to delete both 'SUV' and 'sedan'. The following code deletes 'sedan' and 'truck' instead of 'sedan' and 'SUV' ; let arr = ['excavator','SUV','crossover','sedan','truck'] ; arr = arr.map((ele,ind,ar) => { if (ele === 'crossover'){ ar.splice(ind+1,1); ar.splice(ind-1,1); } return ele + ": Sold."; }); return arr; //it produces ['excavator: Sold.','SUV: Sold.','crossover: Sold.',,]
You can first remove the elements and then map the array, here is an example: let arr = ['excavator','SUV','crossover','sedan','truck']; arr.splice(arr.indexOf('crossover') - 1, 3, 'crossover'); arr = arr.map((ele) => { return ele + ": Sold."; }); console.log(arr); The .splice(..) call replaces SUV, crossover and sedan with crossover
You should not remove elements on a .map interation since the array size will change as well as the array indexes. You should try something like this: let arr = ['excavator','SUV','crossover','sedan','truck'] ; arr = arr.filter(element => element != "crossover"); arr = arr.map(ele => `${ele} :Sold.`); console.log(arr); Or, from an old school perspective: let arr = ['excavator','SUV','crossover','sedan','truck']; let newArr=[] for (var i=0; i<arr.length; i++) if(arr[i] !== 'crossover') newArr.push(`${arr[i]} :Sold.`) console.log(newArr);
itus has the correct anwer, IMO but for the sake of fun this is an alternative way: let arr = ['excavator','SUV','crossover','sedan','truck'] ; let suvIndex = null; const result = arr.map((item, index) => { if (item === "SUV") { suvIndex = index; } if(suvIndex && index <= suvIndex + 2) { return null; } return item; }).filter(chunk => chunk) console.log(result); https://jsfiddle.net/vbz14fw6/
How to Splice in a javascript array based on property?
I am getting an array of data in Angularjs Grid and I need to delete all the rows which has same CustCountry ex - My Customer Array looks like Customer[0]={ CustId:101 ,CustName:"John",CustCountry:"NewZealand" }; Customer[1]={ CustId:102 ,CustName:"Mike",CustCountry:"Australia" }; Customer[2]={ CustId:103 ,CustName:"Dunk",CustCountry:"NewZealand" }; Customer[3]={ CustId:104 ,CustName:"Alan",CustCountry:"NewZealand" }; So , in the Grid I need to delete all three records if CustomerCountry is NewZealand I am using splice method and let me know how can I use by splicing through CustomerCountry $scope.remove=function(CustCountry) { $scope.Customer.splice(index,1); }
If you're okay with getting a copy back, this is a perfect use case for .filter: Customer = [ { CustId:101 ,CustName:"John",CustCountry:"NewZealand" }, { CustId:102 ,CustName:"Mike",CustCountry:"Australia" }, { CustId:103 ,CustName:"Dunk",CustCountry:"NewZealand" }, { CustId:104 ,CustName:"Alan",CustCountry:"NewZealand" }, ] console.log(Customer.filter(cust => cust.CustCountry !== "NewZealand"));
if you have one specific country in mind then just use .filter() $scope.Customer = $scope.Customer.filter(obj => obj.CustCountry !== "SpecificCountry") If you want to delete all objects with duplicate countries then, referring to Remove duplicate values from JS array, this is what you can do: var removeDuplicateCountries = function(arr){ var dupStore = {}; for (var x= 0; x < arr.length; x++){ if (arr[x].CustCountry in dupStore){ dupStore[arr[x].CustCountry] = false; } else { dupStore[arr[x].CustCountry] = true; } } var newarr = []; for (var x= 0; x < arr.length; x++){ if (dupStore[arr[x].CustCountry]){ newarr.push(arr[x]); } } return arr; }; $scope.Customer = removeDuplicateCountries($scope.Customer); Or incorporating the .filter() method var removeDuplicateCountries = function(arr){ var dupStore = {}; var newarr = arr; for (var x= 0; x < arr.length; x++){ if (arr[x].CustCountry in dupStore){ newarr = newarr.filter(obj => obj.CustCountry !== arr[x].CustCountry); } else { dupStore[arr[x].CustCountry] = true; } } return newarr; }; $scope.Customer = removeDuplicateCountries($scope.Customer); if there are many duplicate countries then use the way without .filter()
Get the index of a multidimensional array with the value of a given string in javascript
I have this array, var arr = [["present",["John","Josh","Jay"]],["absent",["May","Mary","Mary Jane"]]]; var arr2 = [["J",["John","Josh","Jay"]],["M",["May","Mary","Mary Jane"]]]; And I want to get the data[0] of the array that have value of "Mary". So in my example, value that I will get is "absent". I want also to get the index of the array arr2 that have value of "Josh". So in my second array, value that I will get is 0. If possible to use underscore js, we can use it. I tried to use _.contains() but failed. Also these array is used in knockout js.
One other way of doing this job could be as follows; var a1 = [["present",["John","Josh","Jay"]],["absent",["May","Mary","Mary Jane"]]], a2 = [["J",["John","Josh","Jay"]],["M",["May","Mary","Mary Jane"]],["S",["Sally","Sam","Sammy Davis"]]], getStatus = (a,n) => a.find(e => e[1].indexOf(n) !== -1)[0], getIndex = (a,n) => a.findIndex(e => e[1].indexOf(n) !== -1); console.log(getStatus(a1,"Mary")); console.log(getIndex(a2,"Sammy Davis"));
var arr = [["present",["John","Josh","Jay"]],["absent",["May","Mary","Mary Jane"]]]; var arr2 = [["J",["John","Josh","Jay"]],["M",["May","Mary","Mary Jane"]]]; arr.forEach(function(e,i,a){ if(e[1].indexOf("Mary")>-1){ console.log(e[0]) } }); arr2.forEach(function(e,i,a){ if(e[1].indexOf("Josh")>-1){ console.log(e[0]) } });
First using filter() and second findIndex() var arr = [["present",["John","Josh","Jay"]],["absent",["May","Mary","Mary Jane"]]]; var result1 = arr.filter(x => x[1].indexOf("Mary") !== -1)[0][0]; console.log(result1); // absent var arr2 = [["J",["John","Josh","Jay"]],["M",["May","Mary","Mary Jane"]]]; var result2 = arr.findIndex(x => x[1].indexOf("Josh") !== -1); console.log(result2); // 0
If your data set isn't really huge you can store two map objects in memory to quickly access values. Note that this won't work for duplicate names. The benefit of this approach is that you only have to loop through each array once. If you use any method based on indexOf, you'll loop through your data every time you retrieve a value. var arr = [["present",["John","Josh","Jay"]],["absent",["May","Mary","Mary Jane"]]]; var arr2 = [["J",["John","Josh","Jay"]],["M",["May","Mary","Mary Jane"]]]; var makeKeyMap = function(arr) { return arr.reduce(function(map, data) { data[1].forEach(function(key) { map[key] = data[0]; }); return map; }, {}); }; var makeIndexMap = function(arr) { return arr.reduce(function(map, data, index) { data[1].forEach(function(key) { map[key] = index; }); return map; }, {}); }; var arrMap = makeKeyMap(arr); var arr2Map = makeIndexMap(arr2); console.log(arrMap["Mary"]); console.log(arr2Map["Josh"]); Edit: a performance test var myTestData = createTestData(); var randomNameToFind = (function() { var namesToFind = ["Aileen","Christina","Donna","Judith","Mandy","Sandra","Dawn","Tracey","Mhairi","Victoria","Carolyn","Gayle","Maria","Valerie"]; return function() { return namesToFind[Math.floor(Math.random() * namesToFind.length)]; } }()); console.log("Finding the number index for a random name out of 800 names, 10000 times:"); console.time("using index of approach"); var usingIndexOf = (a,n) => a.findIndex(e => e[1].indexOf(n) !== -1); var results = []; for (var i = 0; i < 10000; i += 1) { results.push(usingIndexOf(myTestData, randomNameToFind())); } console.timeEnd("using index of approach"); console.time("using map approach"); var makeIndexMap = function(arr) { return arr.reduce(function(map, data, index) { data[1].forEach(function(key) { map[key] = index; }); return map; }, {}); }; var myMap = makeIndexMap(myTestData); results = []; for (var j = 0; j < 10000; j += 1) { results.push(myMap[randomNameToFind()]); } console.timeEnd("using map approach"); console.log("index map size: " + sizeof(myMap) + " bytes"); // Random data generation code below function createTestData() { var names = ["Nicola","Karen","Fiona","Susan","Claire","Sharon","Angela","Gillian","Julie","Michelle","Jacqueline","Amanda","Tracy","Louise","Jennifer","Alison","Sarah","Donna","Caroline","Elaine","Lynn","Margaret","Elizabeth","Lesley","Deborah","Pauline","Lorraine","Laura","Lisa","Tracey","Carol","Linda","Lorna","Catherine","Wendy","Lynne","Yvonne","Pamela","Kirsty","Jane","Emma","Joanne","Heather","Suzanne","Anne","Diane","Helen","Victoria","Dawn","Mary","Samantha","Marie","Kerry","Ann","Hazel","Christine","Gail","Andrea","Clare","Sandra","Shona","Kathleen","Paula","Shirley","Denise","Melanie","Patricia","Audrey","Ruth","Jill","Lee","Leigh","Catriona","Rachel","Morag","Kirsten","Kirsteen","Katrina","Joanna","Lynsey","Cheryl","Debbie","Maureen","Janet","Aileen","Arlene","Zoe","Lindsay","Stephanie","Judith","Mandy","Jillian","Mhairi","Barbara","Carolyn","Gayle","Maria","Valerie","Christina","Marion","Nicola","Karen","Susan","Claire","Fiona","Angela","Sharon","Gillian","Julie","Jennifer","Michelle","Louise","Lisa","Amanda","Donna","Tracy","Alison","Elaine","Jacqueline","Sarah","Caroline","Elizabeth","Laura","Lynn","Deborah","Lesley","Margaret","Joanne","Pauline","Lorraine","Carol","Kirsty","Yvonne","Lorna","Emma","Lynne","Tracey","Heather","Catherine","Pamela","Helen","Linda","Jane","Anne","Kerry","Suzanne","Wendy","Victoria","Diane","Mary","Dawn","Clare","Gail","Paula","Ann","Shona","Hazel","Christine","Andrea","Samantha","Marie","Lynsey","Sandra","Denise","Lee","Kelly","Gayle","Debbie","Jill","Kathleen","Patricia","Joanna","Catriona","Shirley","Ruth","Zoe","Leigh","Rachel","Melanie","Kirsteen","Aileen","Christina","Janet","Katrina","Stephanie","Audrey","Kirsten","Arlene","Maureen","Morag","Marion","Mhairi","Allison","Cheryl","Maria","Kim","Anna","Lindsay","Rebecca","Katherine","Mandy","Nicola","Karen","Claire","Angela","Fiona","Susan","Jennifer","Julie","Gillian","Michelle","Sharon","Sarah","Louise","Donna","Laura","Amanda","Alison","Lisa","Caroline","Kirsty","Jacqueline","Elaine","Lesley","Lynn","Deborah","Elizabeth","Joanne","Emma","Tracy","Lorraine","Lynne","Margaret","Heather","Carol","Lorna","Pauline","Kelly","Helen","Catherine","Linda","Victoria","Suzanne","Kerry","Pamela","Lee","Wendy","Jane","Yvonne","Tracey","Anne","Clare","Mary","Diane","Christine","Lynsey","Samantha","Shona","Andrea","Marie","Gail","Melanie","Dawn","Ann","Paula","Jill","Ruth","Leigh","Hazel","Debbie","Joanna","Denise","Lindsay","Gayle","Patricia","Catriona","Kathleen","Sandra","Leanne","Stephanie","Rachel","Katrina","Shirley","Kirsteen","Janet","Arlene","Zoe","Jillian","Anna","Judith","Mhairi","Natalie","Audrey","Carolyn","Morag","Aileen","Cheryl","Rebecca","Allison","Barbara","Mandy","Claire","Nicola","Karen","Angela","Gillian","Fiona","Jennifer","Laura","Susan","Julie","Michelle","Lisa","Sharon","Louise","Sarah","Tracy","Donna","Kelly","Kirsty","Amanda","Alison","Joanne","Caroline","Emma","Jacqueline","Elaine","Elizabeth","Lynne","Lesley","Deborah","Kerry","Victoria","Carol","Catherine","Lynn","Pauline","Margaret","Lorna","Lynsey","Lorraine","Linda","Suzanne","Tracey","Heather","Yvonne","Jane","Dawn","Mary","Helen","Anne","Wendy","Lee","Pamela","Jill","Lindsay","Clare","Christine","Diane","Leigh","Samantha","Shona","Joanna","Ruth","Debbie","Gail","Marie","Andrea","Paula","Kathleen","Catriona","Katrina","Denise","Melanie","Ann","Sandra","Gayle","Hazel","Jillian","Stephanie","Rachel","Kim","Natalie","Katherine","Patricia","Leanne","Cheryl","Mhairi","Morag","Arlene","Zoe","Kathryn","Aileen","Ashley","Judith","Anna","Frances","Janet","Lucy","Vicky","Christina","Kirsten","Rebecca","Nicola","Claire","Laura","Karen","Michelle","Louise","Jennifer","Fiona","Lisa","Gillian","Angela","Julie","Susan","Sarah","Kelly","Donna","Sharon","Emma","Caroline","Alison","Joanne","Tracy","Kirsty","Lynne","Amanda","Elaine","Jacqueline","Lesley","Kerry","Elizabeth","Lynn","Margaret","Deborah","Catherine","Heather","Lorna","Yvonne","Carol","Lorraine","Suzanne","Lynsey","Victoria","Helen","Linda","Pauline","Dawn","Anne","Jane","Tracey","Clare","Mary","Diane","Jill","Denise","Lee","Leanne","Christine","Shona","Pamela","Samantha","Paula","Joanna","Debbie","Stacey","Hazel","Cheryl","Lindsay","Gail","Rachel","Marie","Ann","Catriona","Andrea","Ruth","Kathryn","Katrina","Mhairi","Wendy","Leigh","Gayle","Melanie","Sandra","Stephanie","Anna","Jillian","Amy","Carolyn","Patricia","Carrie","Natalie","Kathleen","Lyndsey","Ashley","Rebecca","Vicky","Christina","Lindsey","Katherine","Arlene","Sara","Laura","Claire","Nicola","Lisa","Louise","Michelle","Fiona","Karen","Gillian","Jennifer","Emma","Angela","Susan","Kelly","Julie","Donna","Sarah","Kirsty","Sharon","Joanne","Amanda","Tracy","Alison","Elizabeth","Caroline","Elaine","Jacqueline","Lynne","Leanne","Deborah","Lesley","Lorraine","Victoria","Lynn","Pamela","Kerry","Lynsey","Lorna","Carol","Margaret","Heather","Helen","Catherine","Suzanne","Tracey","Yvonne","Cheryl","Linda","Pauline","Debbie","Jane","Dawn","Clare","Lindsay","Mary","Shona","Anne","Rachel","Jill","Christine","Natalie","Samantha","Diane","Lee","Wendy","Joanna","Paula","Marie","Ann","Denise","Catriona","Gayle","Hazel","Kathleen","Stacey","Gail","Ashley","Andrea","Ruth","Anna","Jillian","Leigh","Katrina","Stephanie","Mhairi","Katherine","Sandra","Lyndsey","Christina","Lucy","Patricia","Carrie","Rebecca","Kathryn","Lyndsay","Melanie","Amy","Sara","Arlene","Kirsten","Laura","Claire","Lisa","Nicola","Louise","Karen","Fiona","Jennifer","Michelle","Emma","Sarah","Gillian","Kelly","Susan","Angela","Donna","Kirsty","Julie","Pamela","Joanne","Caroline","Amanda","Tracy","Sharon","Lynne","Elaine","Deborah","Jacqueline","Alison","Lynsey","Victoria","Kerry","Leanne","Lorraine","Lesley","Elizabeth","Lorna","Catherine","Lynn","Suzanne","Heather","Helen","Lindsay","Margaret","Clare","Cheryl","Debbie","Pauline","Dawn","Carol","Mary","Natalie","Linda","Jane","Diane","Stacey","Carrie","Yvonne","Rebecca","Christine","Marie","Charlene","Rachel","Anne","Tracey","Jill","Samantha","Ashley","Paula","Joanna","Stephanie","Andrea","Shona","Denise","Anna","Hazel","Katrina","Ruth","Gayle","Lee","Sara","Catriona","Kathryn","Leigh","Mhairi","Wendy","Amy","Jillian","Katherine","Gail","Linsey","Christina","Lucy","Melanie","Sandra","Ann","Kathleen","Shelley","Kirsten","Kim","Lyndsey","Laura","Claire","Lisa","Nicola","Emma","Louise","Jennifer","Michelle","Sarah","Fiona","Karen","Gillian","Kirsty","Donna","Kelly","Pamela","Susan","Julie","Angela","Amanda","Lynsey","Sharon","Lynne","Deborah","Joanne","Victoria","Caroline","Alison","Leanne","Gemma","Elaine","Jacqueline","Lesley","Elizabeth","Lorraine","Kerry","Heather","Debbie","Catherine","Lynn","Lorna","Tracy","Suzanne","Yvonne","Cheryl","Natalie","Margaret","Lindsay","Diane","Helen","Pauline","Ashley","Rachel","Clare","Carol","Christine","Linda","Dawn","Rebecca","Stephanie","Jill","Tracey","Jane","Stacey","Paula","Shona","Anna","Charlene","Anne","Marie","Catriona","Samantha","Joanna","Ruth","Andrea","Mary","Denise","Kim","Mhairi","Hazel","Lauren","Amy","Kathryn","Carrie","Lyndsey","Lucy","Gail","Katherine","Christina","Linsey","Wendy","Katrina","Kimberley","Ann","Lee"]; var nameMap = names.reduce((map, n) => { map[n[0]] = map[n[0]] || []; map[n[0]].push(n); return map; }, {}); var testData = Object.keys(nameMap) .sort() .reduce((res, k) => { res.push([k, nameMap[k]]); return res; }, []); return testData; }; <script src="http://code.stephenmorley.org/javascript/finding-the-memory-usage-of-objects/sizeof.compressed.js"></script>
Best way to group elements in an array with least complexity
I have a JSON array which looks like this: var map_results = [{"Type":"Flat","Price":100.9}, {"Type":"Room","Price":23.5}, {"Type":"Flat","Price":67.5}, {"Type":"Flat","Price":100.9} {"Type":"Plot","Price":89.8}] This array contains about 100,000 records. I want the output to be grouped by "Type" and "Price". It should look like this: var expected_output = [{"Type":"Flat", "Data":[{"Price":100.9, "Total":2}, {"Price":67.5, "Total":1}] }, {"Type":"Room","Data":[{"Price":23.5,"Total":1}]}, {"Type":"Plot","Data":[{"Price":89.8, "Total:1"}]}] This has to be done in pure javascript and I cannot use libraries like undersore.js. I tried solving the problem but it had like 3 nested for loops which made the complexity as n^4. What could be a better solution for this problem?? The function I have looks like this: var reduce = function (map_results) { var results = []; for (var i in map_results) { var type_found = 0; for(var result in results){ if (map_results[i]["Type"] == results[result]["Type"]){ type_found = 1; var price_found = 0; for(var data in results[result]["Data"]){ if(map_results[i]["Price"] == results[result]["Data"][data]["Price"]){ price_found = 1; results[result]["Data"][data]["Total"] +=1; } } if(price_found == 0){ results[result]["Data"].push({"Price":map_results[i]["Price"], "Total":1}); } } } if(type_found == 0){ results.push({"Type":map_results[i]["Type"], "Data":[{"Price":map_results[i]["Price"],"Total":1}]}); } } return results; };
I have a short function that handles the first part of the requested functionality: It maps the map_results to the desired format: var map_results = [{"Type":"Flat","Price":100.9}, {"Type":"Room","Price":23.5}, {"Type":"Flat","Price":67.5}, {"Type":"Flat","Price":100.9}, {"Type":"Plot","Price":89.8}] var expected_output = map_results.reduce(function(obj, current){ if(!obj[current.Type]){ obj[current.Type] = {'Type':current.Type, 'Data':[]}; } obj[current.Type].Data.push({'Price':current.Price, 'Total':1}); return obj; },{}) Then this piece of code is required to calculate the totals, I'm afraid: for(var type in expected_output){ var d = {}; for(var item in expected_output[type].Data){ d[expected_output[type].Data[item].Price] = (d[expected_output[type].Data[item].Price] || 0) + 1; } expected_output[type].Data = []; for(var i in d){ expected_output[type].Data.push({ 'Price':i, 'Total':d[i] }) } } Output: { "Flat":{ "Type":"Flat", "Data":[{"Price":"100.9","Total":2}, {"Price":"67.5","Total":1}] }, "Room":{ "Type":"Room", "Data":[{"Price":"23.5","Total":1}] }, "Plot":{ "Type":"Plot", "Data":[{"Price":"89.8","Total":1}] } }
As the Types and the Prices are unique after grouping I think a structure like {"Flat": {"100.9":2,"67.5":1}, {"Room": {"23.5": 1}}} would be easier to handle. So could do the grouping the following way: var output = {}; map_results.map(function(el, i) { output[el["Type"]] = output[el["Type"]] || []; output[el["Type"]][el["Price"] = (output[el["Type"]][el["Price"]+1) || 1; }); If you can not handle this structure you could do another mapping to your structure. As you are iterating the Array one time this should have a complexity of n. Look here for a working fiddle. EDIT: So remap everything to your structure. The order of the remapping is far less then the first mapping, because the grouping is already done. var expected_output = []; for(type in output) { var prices = []; for(price in output[type]) { prices.push({"Price": price, "Total": output[type][price]); } expected_output.push({"Type": type, "Data": prices}); }
Below is yet another effort. Here's a FIDDLE For performance testing, I also mocked up a JSPerf test with 163840 elements. On Chrome(OSX) original solution is 90% slower than this one. Few notes: Feel free to optimize for your case (e.g. take out the hasOwnProperty check on object cloning). Also, if you need the latest Total as the first element use unshift instead of push to add the obj the beginning of the array. function groupBy(arr, key, key2) { var retArr = []; arr.reduce(function(previousValue, currentValue, index, array){ if(currentValue.hasOwnProperty(key)) { var kVal = currentValue[key]; if(!previousValue.hasOwnProperty(kVal)) { previousValue[kVal] = {}; retArr.push(previousValue[kVal]); previousValue[kVal][key] = kVal; previousValue[kVal]["Data"] = []; } var prevNode = previousValue[kVal]; if(currentValue.hasOwnProperty(key2)) { var obj = {}; for(var k in currentValue) { if(currentValue.hasOwnProperty(k) && k!=key) obj[k] = currentValue[k]; } obj["Total"] = prevNode["Data"].length + 1; prevNode["Data"].push(obj); } } return previousValue; }, {}); return retArr; } var map_results = [{"Type":"Flat","Price":100.9}, {"Type":"Room","Price":23.5}, {"Type":"Flat","Price":67.5}, {"Type":"Flat","Price":100.9}, {"Type":"Plot","Price":89.8}]; var expected_output = groupBy(map_results, "Type", "Price"); console.dir(expected_output);
Tried something like this: var reduce_func = function (previous, current) { if(previous.length == 0){ previous.push({Type: current.Type, Data:[{Price:current.Price,Total:1}]}); return previous; } var type_found = 0; for (var one in previous) { if (current.Type == previous[one].Type){ type_found = 1; var price_found = 0; for(var data in previous[one].Data){ if(current.Price == previous[one].Data[data].Price){ price_found = 1; previous[one].Data[data].Total += 1; } } if(price_found == 0){ previous[one].Data.push({Price:current.Price, Total:1}); } } } if(type_found == 0){ previous.push({Type:current.Type, Data:[{Price : current.Price ,Total:1}]}); } return previous; } map_results.reduce(reduce_func,[]);