I have JSON as
var newJSON = [{
"key": "India",
"value": "72"
}, {
"key": "India",
"value": "27"
}, {
"key": "Pakistan",
"value": "90"
}, {
"key": "Zimbamwe",
"value": "88"
}, {
"key": "India",
"value": "100"
}, {
"key": "Pakistan",
"value": "172"
}]
I want desired result as below, where the duplicate key values have their value properties added together:
[{
"key": "India",
"value": "199"
}, {
"key": "Pakistan",
"value": "262"
}, {
"key": "Zimbamwe",
"value": "88"
}]
Please help me with this
Here is the solution:
var grouped = [];
var added = [];
for(var i = 0; i < json.length; i++) {
var indexOfCountry = added.indexOf(json[i].key);
if (indexOfCountry >= 0)
{
grouped[indexOfCountry].value = (Number(grouped[indexOfCountry].value) + Number(json[i].value)).toString();
}
else {
grouped.push(json[i]);
added.push(json[i].key);
}
}
grouped array is your desired result.
Fiddle: https://jsfiddle.net/zummw9zp/
Yet another variant with reduce
var result = newJSON.reduce(function(acc, el){
var val = acc.map[el.key];
if(!val){
acc.map[el.key] = val = { key:el.key, value: parseInt(el.value) };
acc.result.push(val);
}else{
val.value += parseInt(el.value);
}
return acc;
},{map:{}, result:[]}).result;
var newJSON = [
{"key":"India","value":"72"},{"key":"India","value":"27"},
{"key":"Pakistan","value":"90"},{"key":"Zimbamwe","value":"88"},
{"key":"India","value":"100"},{"key":"Pakistan","value":"172"}
];
document.getElementById('r').innerHTML = 'newJSON: ' + JSON.stringify(newJSON);
var result = newJSON.reduce(function(acc, el){
var val = acc.map[el.key];
if(!val){
acc.map[el.key] = val = { key:el.key, value: parseInt(el.value) };
acc.result.push(val);
}else{
val.value += parseInt(el.value);
}
return acc;
},{map:{}, result:[]}).result;
document.getElementById('r').innerHTML += '<br /><br />result: ' + JSON.stringify(result);
<div id="r"></div>
This is a classic use case for reduce, which is designed to take arrays and somehow, well, "reduce" them to other things, by looping across them and transforming the result at each iteration.
return newJSON.reduce(function(result, entry) {
key = entry.key;
result[key] = result[key] || { key: key, value: 0 };
result[key].value += entry.value;
return result;
}, {});
Using Underscore
If you're OK with using a library like Underscore, you can write this as
_.mapObject(_.groupBy(newJSON, 'key'), total)
Using a narrative style where we describe
`A(B, C)`
as
Take B and do A to it usingC
and
`A(B(C))`
as
Take C and do B to it. Then take the result and do A to it
we can almost read this as English:
Take newJSON and group it by using key. Then take the result and map the object using total
_.groupBy produces an object keyed by some property and returns groups, which are arrays of all the entries falling into each group:
{
India: [ {key: "India", value: 72}, {key: "India", value: 100... ],
...
}
total calculates the total value for each group:
function total(group) { return sum(group . map(value)); }
So it converts an array of entries
[ {key: "India", value: 72}, {key: "India", value: 100}, ... ],
into 199. We use this to map the arrays of entries to total scores using _.mapObject.
Here sum can be written as
function sum(array) { return array.reduce(add); }
function add(a, b) { return a+b; }
and value is a little utility function to retrieve the value property:
function value(entry) { return entry.value; }
So the complete solution is:
function add (a, b) { return a+b; }
function sum (array) { return array.reduce(add); }
function value (entry) { return entry.value; }
function total (group) { return sum(group . map(value)); }
_.mapObject(_.groupBy(newJSON, 'key'), total)
Related
Ok, so I'm getting some data from a server like this: myVar = JSON.parse(xhttp.responseText). Now, my var looks something like:
{
someData1234:{
score: "5",
loc: "NY"
},
someData4321:{
score: "70",
loc: "MH"
},
someData4123:{
score: "43",
loc: "NG"
}
}
How can i sort through the object so it orders it by the score in an descending order? i tried other solutions but didn't seem to work correctly as i need the same object(or an array of objects) back but just re-ordered
As already noted, JavaScript object property order is not guaranteed, so you will have to convert your data into an array of objects and then sort. Following example uses Object.entries and map to convert the initial object into an array, then sorts by parsing a numerical value from score in each object:
const obj = { someData1234: { score: "5", loc: "NY" }, someData4321: { score: "70", loc: "MH" }, someData4123: { score: "43", loc: "NG" } };
const arr = Object.entries(obj).map(([k, v]) => ({[k]: v}));
const sorted = arr.sort((a, b) => {
return parseInt(a[Object.keys(a)[0]].score) - parseInt(b[Object.keys(b)[0]].score);
});
console.log(sorted);
// [{"someData1234":{"score":"5","loc":"NY"}},{"someData4123":{"score":"43","loc":"NG"}},{"someData4321":{"score":"70","loc":"MH"}}]
var array = [{
"EmployeeName": "John",
"Experience": "12",
"Technology": "SharePoint"
}, {
"EmployeeName": "Charles",
"Experience": "9",
"Technology": "ASP.NET"
}, {
"EmployeeName": "Jo",
"Experience": "3",
"Technology": "JAVA"
}, {
"EmployeeName": "Daine",
"Experience": "7",
"Technology": "Sql Server"
}, {
"EmployeeName": "Zain",
"Experience": "6",
"Technology": "C#"
}];
//Comparer Function
function GetSortOrder(prop) {
return function(a, b) {
if (a[prop] > b[prop]) {
return 1;
} else if (a[prop] < b[prop]) {
return -1;
}
return 0;
}
}
array.sort(GetSortOrder("EmployeeName")); //Pass the attribute to be sorted on
document.write("Sorted Employee Names : ");
for (var item in array) {
document.write("<br>" + array[item].EmployeeName);
}
array.sort(GetSortOrder("Technology")); //Pass the attribute to be sorted on
document.write("<br><br> Sorted Technology Names : ");
for (var item in array) {
document.write("<br>" + array[item].Technology);
}
You could move the keys (like "someData1234") inside the objects (as the value of a "key" property), and put those objects in an array sorted by your liking: that way you have all information at hand:
const data = {someData1234:{score: "5",loc: "NY"},someData4321:{score: "70",loc: "MH"},someData4123:{score: "43",loc: "NG"}};
const result = Object.entries(data).map(([key, val]) => ({key, ...val}))
.sort((a,b) => a.score - b.score);
console.log(result);
let something = {
someData1234: {
score: "5",
loc: "NY"
},
someData4321: {
score: "70",
loc: "MH"
},
someData4123: {
score: "43",
loc: "NG"
}
};
let someArray = [];
let myArray = []
Object.keys(something).map((som, ind) => {
myArray.push(`${Object.values(Object.entries(something)[ind][1])[0]}` + ind);
});
myArray.sort((a, b) => {
return a - b
});
console.log(myArray);
for (let i = 0; i < myArray.length; i++) {
let obj=Object.entries(something)[myArray[i].charAt(myArray[i].length-1)];
console.log(obj);
let key= obj[0];
let value=obj[1];
someArray.push({[key]:value})
}
let myOrderedObject;
let obj;
for(let i=0;i<someArray.length;i++){
obj=someArray[i];
console.log(obj);
myOrderedObject={...myOrderedObject,...obj};
console.log(myOrderedObject);
console.log(i);
}
console.log('mY ordered Object ',myOrderedObject);
console.log(someArray);
Hope it helps...
const data = {
someData1234:{
score: "5",
loc: "NY"
},
someData4321:{
score: "70",
loc: "MH"
},
someData4123:{
score: "43",
loc: "NG"
}
};
// Use an Intl.Collator to construct a function that will sort number strings
const naturalSort = new Intl.Collator(undefined, { numeric: true }).compare;
const sortProperties = obj => Object.getOwnPropertyNames(obj) // Get the list of properties
.sort((a, b) => naturalSort(obj[a].score, obj[b].score)) // Order based on the score
.reduce( // Reduce the ordered properties to create a new object
(result, prop) => {
result[prop] = obj[prop];
return result;
}, {}
);
console.log(sortProperties(data));
I have an array object call listOfObjects.
[{"name":"A", "data":"[{"value1":"1","value2":"2"}]"},
{"name":"B", "data":"[{"value1":"1","value2":"2"}]"}]
What I want to do is insert an object into the array where the array is empty.If the array is not empty then do a check on the item inside. If item already exist, do update on the item, else add it to the array. Below is my code
var searchName= "A";
if (listOfObjects.length > 0) {
for (var i = 0; i < listOfObjects.length; i++) {
if (listOfObjects[i].name == searchName) {
listOfObjects[i].data = data;
break;
} else {
insert = {
'name': searchName,
'data': data
};
listOfObjects.push(insert);
}
}
} else {
insert = {
'name': searchName,
'data': data
};
listOfObjects.push(insert);
}
When I run it, even though A already exist, it update the existing item but also add one more time to the listOfObjects. Is there anyway that can achieve what I want? Thanks..
The problem is you're inserting into the array inside your for loop looking for a match. Instead, remember whether you've seen a match and insert after the loop if you haven't. There's also no reason for the length check and no reason to repeat your logic for inserting:
var searchName= "A";
var found = false;
for (var i = 0; !found && i < listOfObjects.length; i++) {
if (listOfObjects[i].name == searchName) {
listOfObjects[i].data = data;
found = true;
}
}
if (!found) {
listOfObjects.push({
'name': searchName,
'data': data
});
}
Note that you can use Array#find (which can be polyfilled for old browsers) to find the entry rather than a for loop if you like:
var searchName= "A";
var entry = listOfObjects.find(function(entry) {
return entry.name == searchName;
});
if (entry) {
entry.data = data;
} else {
listOfObjects.push({
'name': searchName,
'data': data
});
}
First of all change this
[{"name":"A", "data":"[{"value1":"1","value2":"2"}]"},
{"name":"B", "data":"[{"value1":"1","value2":"2"}]"}]
by
[{"name":"A", "data":[{"value1":1,"value2":2}]},
{"name":"B", "data":[{"value1":"1","value2":"2"}]}];
because your list will throw Uncaught SyntaxError: Unexpected identifier
Write another simple function to get the item listOfObjects[i] with selected searchName. Here 'getSearchObject()' function checks the existance of searchName and then add or updates array.
addOrRemoveItem() {
let listOfObjects = [
{ "name": "A", "data": "[{'value1':'1','value2':'2'}]" },
{ "name": "B", "data": "[{'value1':'1','value2':'2'}]" }
],
data = '[{"value1":"1","value2":"2"}]';
var searchName = "C";
if (listOfObjects.length > 0) {
let searchObj = this.getSearchObject(listOfObjects, searchName);
if (searchObj) {
searchObj.data = data;
} else {
let insert = {
"name": searchName,
"data": data
}
listOfObjects.push(insert);
}
} else {
let insert = {
"name": searchName,
"data": data
}
listOfObjects.push(insert);
}
}
getSearchObject(objArr, searchKey) {
var obj = null;
for (let i = 0; i < objArr.length; i++) {
if (objArr[i].name === searchKey) {
obj = objArr[i];
}
}
return obj;
}
A generic solution that recognizes older JS engines (filter instead of find) but does always assume getting passed a list of unique items could be implemented like this ...
function updateList(itemList, item) { // - always assume a list of unique items.
var
itemName = item.name,
listItem = itemList.filter(function (elm) { // - assume `filter`, not find`.
return (elm.name === itemName); // - find/get existing list item by name.
})[0];
if (listItem) {
listItem.data = item.data;
} else {
itemList.push(item)
}
}
var list = [
{ "name": "A", "data": [ { "value1": "A1", "value2": "A2" }] },
{ "name": "B", "data": [ { "value1": "B1", "value2": "B2" }] },
{ "name": "C", "data": [ { "value1": "C1", "value2": "C2" }] }
];
console.log('list : ', list);
updateList(list, { "name": "D", "data": [ { "value1": "D1", "value2": "D2" }] });
updateList(list, { "name": "B", "data": [ { "value1": "b_1", "value2": "b_2", "value3": "b_3" }] });
updateList(list, { "name": "C", "data": [ { "value3": "C3" }] });
console.log('list : ', list);
.as-console-wrapper { max-height: 100%!important; top: 0; }
I am unable to find a reasonable solution as I am pulling JSON data from firebase and pulling it from node.js into an html file. I want to sort my data via a property called "value" by not sure how to access a sub-sub-value to sort by in JQuery and am wondering if someone could help lead me in the right direction.
{
key: "a",
{
key: "ab",
{
value: 2
}
key: "ac",
{
value: 0
}
}
},
{
key: "b",
{
key: "bb",
{
value: 1
}
}
},
Output:
[{ac}, {bb}, {ab}]
Both your input and desired output are expressed in an invalid notation (both in JSON and JavaScript syntax), so I'll have to make some assumptions.
You could use this recursive function, which will find all nested value properties, and collect those values together with the names of the parent properties in which they occur. Finally those pairs of data (parent key and value) are sorted:
function collectValues(obj, name) {
return Object.entries(obj).reduce( (acc, [key, value]) => {
return acc.concat(
// recursively look into nested objects:
Object(value) === value ? collectValues(value, key)
// else, when key is 'value', collect that object
: key == 'value' ? [[name, value]]
// otherwise ignore this value
: []
)
}, []);
}
// Sample input
var input = [{
"a": {
"ab": {
value: 2
},
"ac": {
value: 0
}
}
}, {
"b": {
"bb": {
value: 1
}
}
}];
var result = collectValues(input).sort( (a,b) => a[1] - b[1] );
console.log(result);
Mocking up your JSON from the original image:
var data = {
key1: {
"a": { "deliveryshort": "12152017" },
"b": { "deliveryshort": "10122015" },
"c": { "deliveryshort": "11302016" },
"d": { "deliveryshort": "09022014" }
},
key2: {
"a": { "deliveryshort": "10102017" },
"b": { "deliveryshort": "09102017" }
},
};
function parseDate(dateStr) {
var month = "" + dateStr[0] + dateStr[1];
var day = "" + dateStr[2] + dateStr[3];
var year = "" + dateStr[4] + dateStr[5] + dateStr[6] + dateStr[7];
var result = new Date(year, month, day);
return result;
}
function sortBy(data, property, converter) {
var j = new Array();
for (var item in data) {
j.push([item, data[item], converter(data[item][property])]);
}
j.sort(function (a, b) { return a[2] - b[2] });
return j;
}
function sortData(data) {
var d = {};
for (var item in data) {
var sorted = sortBy(data[item], "deliveryshort", function (a) { return parseDate(a); });
/*var normalized = new Array();
for (var i = 0; i < sorted.length; i++) {
var ni = sorted[i];
var key = ni[0];
var obj = ni[1];
normalized[key] = obj;
}*/
d[item] = sorted;
}
console.log(d);
return d;
}
sortData(data);
Say I have an array of json objects which looks like below:
var codes = [{
"code_id": "1",
"code_name": "code 1", ,
}, {
"code_id": "2",
"code_name": "code889",
},
// ... () ...
]
How can I filter codes array based on dynamic input parameter?
So I am looking for a generic function which will take input array and key and value as i/p.
var filteredCodes = getFilteredCodes(codes, "code_id", 2);
Thanks.
Use Array.prototype.filter to filter out the result - see demo below:
var codes = [{"code_id": "1","code_name": "code 1"}, {"code_id": "2","code_name": "code889"}];
function getFilteredCodes(array, key, value) {
return array.filter(function(e) {
return e[key] == value;
});
}
var filteredCodes = getFilteredCodes(codes, "code_id", 2);
console.log(filteredCodes);
You could use Array#filter with the key and value.
function getFilteredCodes(array, key, value) {
return array.filter(function (o) {
return o[key] === value;
});
}
var codes = [{ "code_id": "1", "code_name": "code 1", }, { "code_id": "2", "code_name": "code889" }],
filteredCodes = getFilteredCodes(codes, "code_id", "2");
console.log(filteredCodes);
function getFilteredCodes(poolArray,key,val){
return poolArray.filter(function(item,ind){
return item[key]==val;
});
}
Or the function in an only line with arrow notation
var codes = [{ "code_id": "1", "code_name": "code 1", }, { "code_id": "2", "code_name": "code889" }];
var getFilteredCodes = (array, key, value) => array.filter(x => x[key] === value);
var FilteredCodes = getFilteredCodes(codes, "code_id", "2");
console.log(FilteredCodes);
I have a series of JSON entries:
[{"num": "1","name_A": "Alex" ,"name_B": "Bob"}, {"num": "2","name_A": "Anne" ,"name_B": "Barbra"}]
I am trying to convert this array of Objects as painlessly as possible into two objects - one with title name_A, and the second with the title name_B. Objects have to contain the title and an array of matching num-name pairs:
[{title: "name_A", names:[{"1", "Alex}, {"2", "Anne"}]}, {title:"name_B", names: [{"1", "Bob"}, {"2", "Barbra"}]}]
At first I tried simply to create two objects by reducing the array of object twice, once for name_A and second time for name_B and later glue everything together:
// get 'names' array
var name_A = objArray.reduce(function(memo, curr) {
memo.push({curr.num, curr.name_A})
return memo;
}, []);
But even this is failing. Why there is no push method for memo if I initialize reduce with an empty array?
And second question, am I on a right track or is there a better way to achieve this?
Comments inline, made a few minor corrections to the expectations.
var input = [{ "num": "1", "name_A": "Alex", "name_B": "Bob" }, { "num": "2", "name_A": "Anne", "name_B": "Barbra" }]
var output = input.reduce(function (a, b) {
// construct new objects and set their properties
var i = {};
i[b.num] = b.name_A;
var j = {};
j[b.num] = b.name_B;
// add them to our collection elements
a[0].names.push(i);
a[1].names.push(j);
return a;
// initializing our collection
}, [{ title: "name_A", names: [] }, { title: "name_B", names: [] }]);
// pretty print our output
console.log(JSON.stringify(output, null, " "))
var input = [{ "num": "1", "name_A": "Alex", "name_B": "Bob" }, { "num": "2", "name_A": "Anne", "name_B": "Barbra" }]
var output = input.reduce(function (a, b) {
// construct new objects and set their properties
var i = {};
i[b.num] = b.name_A;
var j = {};
j[b.num] = b.name_B;
// add them to our collection elements
a[0].names.push(i);
a[1].names.push(j);
return a;
// initializing our collection
}, [{ title: "name_A", names: [] }, { title: "name_B", names: [] }]);
so.log(output)
<pre id="output"></pre>
<script>
var so = {
log: function(o) {
document.getElementById("output").innerHTML = JSON.stringify(o, null, " ")
}
}
</script>
The problem with your code is that { curr.num, curr.name_A } is not a valid object, it's missing the property names. I've added properties num and name in my code below.
var name_A = [];
var name_B = [];
objArray.forEach(function(curr) {
name_A.push({num: curr.num, name: curr.name_a});
name_B.push({num: curr.num, name: curr.name_B});
});
var result = [
{ title: "name_A" }, names: name_A },
( title: "name_B" }, names: name_B }
];
Also, if you want to make an array out of the results of looping over an array, you should use .map rather than .reduce.
Assuming only property num is fixed. All other properties are treated as data, like name_A or name_B.
var a = [{ "num": "1", "name_A": "Alex", "name_B": "Bob" }, { "num": "2", "name_A": "Anne", "name_B": "Barbra" }],
result = [];
a.forEach(function (el) {
var num = el.num;
Object.keys(el).forEach(function (k) {
function tryFindIndexAndSetNames(aa, i) {
if (aa.title === k) {
result[i].names[num] = el[k];
return true;
}
}
if (k !== 'num' && !result.some(tryFindIndexAndSetNames)) {
var o = {};
o[num] = el[k];
result.push({ title: k, names: o });
}
});
});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');