Sorting JSON Array by a double nested value? - javascript

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);

Related

how to merge two two objects with different depths dynamically

I have two identical objects with me
let a = {
title : "developer”,
startDate:{ month :’jan’}
}
let b = {
title :{
value: ""
} ,
startDate :{month:{value:””}}
}
i need to merge dynamically these two to get object like below
let c = {
title :{
value: "developer"
} ,
startDate:{
month:{ value:” jan”}}
}
You don't require object b because it's just a replica of object a with extra 'value' property.
You can traverse the complete a object and then deep copy the value in the b object.
I wrote a recursive method for this where you can traverse to the last level of the object and copy the value in another object.
function mergeObj(sourceObj, newObj) {
Object.keys(sourceObj).forEach(key => {
if (sourceObj[key] && typeof sourceObj[key] === 'object') {
newObj[key] = {};
mergeObj(sourceObj[key], newObj[key]);
} else {
// updating properties
newObj[key] = {};
newObj[key]['value'] = sourceObj[key];
}
});
}
let a = {
title : "developer",
startDate:{ month :'jan'}
};
let b = {};
mergeObj(a,b);
console.log(b);
You probably need to start by making both object have the same structure, and then run the deep merge. lodash's merge can help you with it
const newA = Object.entries(a).reduce((newObject, [key, value]) => ({
...newObject,
[key]: { value },
}, {}))
// newA looks now like
//
// {
// title: {
// value: "developer
// }
// }
let c = _.merge(a, b); // lodash merge for deep merge. Otherwise write your own
Here is a workaround for your problem:
let a = {
title : "developer",
startDate:{ month :'jan'}
}
let b = {
title :{
value: ''
} ,
startDate :{month:{value:''}}
}
var c = {};
c.startDate = {};
c.title = {};
c.startDate.month = {};
c.startDate.month.value = a.startDate.month;
c.title.value = a.title;
console.log("Merged object",c);
You can just implement a function that does this for you. Given your example:
let a = {
title: "developer",
startDate: { month: "jan" }
};
let b = {
title: {
value: ""
},
startDate: { month: { value: "" }}
};
You can use this to get the values:
const mergeObject = (a, b) => {
b.title.value = a.title;
b.startDate.month.value = a.startDate.month;
return b;
};
If you call now say let c = mergeObject(a, b) c will be
let c = {
title: {
value: "developer"
},
startDate: {
month: { value: "jan" }}
}
Of course this function can be modified to reflect your exact needs.

Format a javascript object into a specific structure

i have a javascript object as follows
obj = {"account_id-0":null,"option_item_id-0":1,"value-0":"wer","account_id-1":null,"option_item_id-1":2,"value-1":"kkk","account_id-2":null,"option_item_id-2":3,"value-2":"qqqqq"
....
"account_id-n":null,"option_item_id-n":6,"value-n":"see"
}
From the above object, i need to create the following structure
{"0": {
account_id: null,
option_item_id: 1,
value: "wer"
},
"1": {
account_id: null,
option_item_id: 2,
value: "kkk"
},
"2": {
account_id: null,
option_item_id: 2,
value: "qqqqq"
},
.
.
.
"n": {
account_id: null,
option_item_id: 6,
value: "see"
}
}
Any idea on how to implement this?
You can iterate through the all the keys, and use Array#reduce to contruct the resultant object.
let obj = {
"account_id-0": null,
"option_item_id-0": 1,
"value-0": "wer",
"account_id-1": null,
"option_item_id-1": 2,
"value-1": "kkk",
"account_id-2": null,
"option_item_id-2": 3,
"value-2": "qqqqq",
"account_id-n": null,
"option_item_id-n": 6,
"value-0": "see"
};
let result = Object.keys(obj).reduce((res, item) => {
let [key, index] = item.split('-');
if (!res[index]) {
res[index] = {};
}
res[index][key] = obj[item];
return res;
}, {});
console.log(result);
var obj = {
"account_id-0": null,
"option_item_id-0": 1,
"value-0": "wer",
"account_id-1": null,
"option_item_id-1": 2,
"value-1": "kkk",
"account_id-2": null,
"option_item_id-2": 3,
"value-2": "qqqqq"
};
var props = [];
function getObj(ind) {
for (var p in props) {
if (ind === p) {
return props[p];
}
}
}
for (var prop in obj) {
var parts = prop.split('-');
var key = parts[0];
var indx = parts[1];
var tmp = getObj(indx);
if (tmp == undefined) {
var x = {};
x[indx] = {};
x[indx][key] = obj[prop];
props.push(x);
} else {
tmp[indx][key] = obj[prop];
}
}
console.log(props);
This should be the straight forward way of maniplulating the object array with simple split() function.
Try this:
var keys = Object.keys(obj), i = 0
var arr = [], o = {}
for(var k in keys) {
if(keys[k].match(/\d*$/m) == i) {
o[keys[k].replace(/-\d*$/m,'')] = obj[keys[k]]
} else {
i++
arr.push(o)
o = {}
}
}
Here I am using an array instead of an object with the keys "0", "1", " 2", ... "n". I think it's way more convenient.

Javascript Fill array with missing object and value

I have an array like bellow each index contains different set of objects,I want to create an uniformal data where object missing in each index will with Value:0 ,
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2},
],
[
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7},
]
];
how can I get an array like bellow using above above array
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2},
{axis:"Sending Money",value:0,id:6},
{axis:"Other",value:0,id:7},
],
[
{axis:"Email",value:0,id:1},
{axis:"Social Networks",value:0,id:2},
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7},
]
];
There are two functions:
getAllEntries that find all objects and stores them into a variable accEntries. Then accEntries is used to search for all occurrences in a sub-array of d. This whole process is done in checkArray.
checkArray is used to fetch all found and not-found entries in d. Both Arrays (found and not-found) are then used to build a new sub-array that contains either found entries with certain values and/or not-found entries with values of 0.
Hope this helps:
var d = [
[
{
axis: 'Email',
value: 59,
id: 1
},
{
axis: 'Social Networks',
value: 56,
id: 2
},
],
[
{
axis: 'Sending Money',
value: 18,
id: 6
},
{
axis: 'Other',
value: 15,
id: 7
},
]
];
function getAllEntries(array) {
var uniqueEntries = [];
array.forEach(function (subarray) {
subarray.forEach(function (obj) {
if (uniqueEntries.indexOf(obj) === - 1) uniqueEntries.push(obj);
});
});
return uniqueEntries;
}
function checkArray(array, acceptedEntries) {
var result = [];
array.forEach(function (subArray) {
var subResult = [];
var foundEntries = [];
subArray.forEach(function (obj) {
if (foundEntries.indexOf(obj.axis) === - 1) foundEntries.push(obj.axis);
});
var notFound = acceptedEntries.filter(function (accepted) {
return foundEntries.indexOf(accepted.axis) === - 1;
});
foundEntries.forEach(function (found) {
subArray.forEach(function (obj) {
if (obj.axis === found) subResult.push(obj);
});
});
notFound.forEach(function (notfound, index) {
subResult.push({
axis: notfound.axis,
value: 0,
id: notfound.id
});
});
result.push(subResult);
});
return result;
}
var accEntries = getAllEntries(d);
var result = checkArray(d, accEntries);
console.log(result);
You can loop over the array to find all the unique objects and then again loop over to push the values that are not present comparing with the array of objects of unique keys.
You can use ES6 syntax to find if an object with an attribute is present like uniKeys.findIndex(obj => obj.axis === val.axis); and the to push with a zero value use the spread syntax like d[index].push({...val, value: 0});
Below is the snippet for the implementation
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2},
],
[
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7},
{axis:"Social Networks",value:89,id:2},
]
];
var uniKeys = [];
$.each(d, function(index, item) {
$.each(item, function(idx, val){
const pos = uniKeys.findIndex(obj => obj.axis === val.axis);
if(pos == - 1) {
uniKeys.push(val);
}
})
})
$.each(d, function(index, item) {
var temp = [];
$.each(uniKeys, function(idx, val){
const pos = item.findIndex(obj => obj.axis === val.axis);
if(pos == - 1) {
d[index].push({...val, value: 0});
}
})
})
console.log(d);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
How about a short shallowCopy function (Object.assign is not available in IE) and otherwise less than 10 new lines of code?
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2}
],
[
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7}
]
];
var newD_0 = [shallowCopy(d[0][0]), shallowCopy(d[0][1]), shallowCopy(d[1][0]), shallowCopy(d[1][1])];
var newD_1 = [shallowCopy(d[0][0]), shallowCopy(d[0][1]), shallowCopy(d[1][0]), shallowCopy(d[1][1])];
newD_0[2].id = 0;
newD_0[3].id = 0;
newD_1[0].id = 0;
newD_1[1].id = 0;
d = [newD_0, newD_1];
function shallowCopy(obj) {
var copy = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = obj[key];
}
}
return copy;
}
console.log(JSON.stringify(d));
RESULT:
[
[
{
"axis":"Email",
"value":59,
"id":1
},
{
"axis":"Social Networks",
"value":56,
"id":2
},
{
"axis":"Sending Money",
"value":18,
"id":0
},
{
"axis":"Other",
"value":15,
"id":0
}
],
[
{
"axis":"Email",
"value":59,
"id":0
},
{
"axis":"Social Networks",
"value":56,
"id":0
},
{
"axis":"Sending Money",
"value":18,
"id":6
},
{
"axis":"Other",
"value":15,
"id":7
}
]
]

add repeating strings value in json array

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)

Filter array of objects

I get an array of objects from a MongoDB through API.
I then need to filter the result furthermore (client side).
I'll work with long lists (could be some thousand of results), each object has about 10 properties with some arrays in it.
Example of an object:
{
_id: xxxxxxx,
foo: [
{ a: "b", c: "d" },
{ a: "b", c: "d" }
],
data: {
a: "b",
c: "d"
}
}
I loop the array async to improve speed:
async.filter(documents, function(value) {
// Search inside the object to check if it contains the given "value"
}, function(results) {
// Will do something with the result array
});
How can I search inside the current object to check if it contains the given value without know in which property I'll find the value?
Though I've not included the async part but I believe overall searching approach could be like this:
// Input Array
var inpArr = [{
id: 1,
foo: [{
a: "dog",
b: "cat"
}]
}, {
id: 2,
foo: [{
a: "kutta",
b: "billi"
}]
}];
var myFilter = function(val, item, index, array) {
var searchResult = scanProperties(item, val);
return searchResult;
};
// Note: pass additional argument to default filter.
// using Function.Prototype.Bind
var filterResult = inpArr.filter(myFilter.bind(null, "dog"));
alert(filterResult);
console.log(filterResult);
// Recursively scan all properties
function scanProperties(obj, val) {
var result = false;
for (var property in obj) {
if (obj.hasOwnProperty(property) && obj[property] != null) {
if (obj[property].constructor == Object) {
result = result || scanProperties(obj[property], val);
} else if (obj[property].constructor == Array) {
for (var i = 0; i < obj[property].length; i++) {
result = result || scanProperties(obj[property][i], val);
}
} else {
result = result || (obj[property] == val);
}
}
}
return result;
};
JS Fiddle Searching an Array of Objects
You can simply iterate through each and every item recursively, like this
var data = {
_id: 1243,
foo: [{
a: "b",
c: "d"
}, {
a: "b",
c: "d"
}],
data: {
a: "b",
c: "d"
}
};
function findValue(value) {
function findItems(document) {
var type = Object.prototype.toString.call(document);
if (type.indexOf("Array") + 1) {
return document.some(findItems);
} else if (type.indexOf("Object") + 1) {
return Object.keys(document).some(function(key) {
return findItems(document[key]);
});
} else {
return document === value;
}
}
return findItems;
}
console.log(findValue('dd')(data));
# false
console.log(findValue('d')(data));
# true

Categories

Resources