I am receiving JSON array, flattening it to display it as a table, but this logic I am using has a flaw, if my object have same column name it will ignore it and will only add first column it finds.
Problem I am trying to solve
Display nested objects into a flat table
Problem I am stuck at
I want to add object's name to column header, which is not happening at the moment.
I think problem is here
function flattenRecord(result, rec) {
return Object.keys(rec).reduce(function(result, key) {
var value = rec[key];
if (value && typeof value === 'object')
flattenRecord(result, value);
else
result[key] = value; // How to add key here so that it will be added to column header ?
return result;
}, result);
}
Complete Code and Working Example
https://jsfiddle.net/1tsu6xt9/14/
I have fixed the issue, I had to change logic of flattening JSON as suspected,
Changed FlattenRecord method to following,
function flattenRecord2(data) {
var result = {};
function recurse(cur, prop) {
if (Object(cur) !== cur) {
result[prop] = cur;
} else if (Array.isArray(cur)) {
for (var i = 0, l = cur.length; i < l; i++)
recurse(cur[i], prop);
//if (l == 0)
//result[prop] = [];
} else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop + "." + p : p);
}
if (isEmpty && prop)
result[prop] = {};
}
}
recurse(data, "");
return result;
}
and following line too,
var rows = testData.reduce(function(rows, row) {
return appendRow(rows, flattenRecord2(row));
}, []);
Related
I am pulling data from an API that is going to return JSON objects of varying lengths and content. It is a mishmash of objects and arrays that can be several levels deep.
I'm trying to get all of the pairs of data and get them into a single, 2D array that I can then put into google sheets using google sheets script.
Here's the code I'm starting with:
function parseTheAPI(rawResponse){
var theparsedJSONdata = JSON.parse(rawResponse)
console.log(theparsedJSONdata)
return theparsedJSONdata
};
Here's the response from the console:
{ '0xabcxyz':
{ products: [ [Object] ],
meta: [ [Object], [Object], [Object] ] } }
There's lots more actual data where it says Object. I understand how to get at individual pieces of data once I know the contents of the object. If the contents of the object change, however, the code would break.
I want to dynamically get at all the various information pairs and use them.
I've looked at SO questions like this and this and tried to figure out if I can just see that data. Here's my attempt at using recursion to figure what's inside:
function logJsonLevelOne(parsedJson){
for(var k in parsedJson){
if(parsedJson[k] instanceof Object){
console.log("parsedJsonOne = "+ parsedJson[k])
var n = Array.isArray(logJsonLevelOne[k])
logJsonLevelOne(parsedJson[k])
} else {
var n = logJsonLevelOne[k] instanceof Array;
console.log(n)
}
}
};
I get some of the data printed, but I don't know how to get to the next level. I don't understand what's coming out of each part of the "if" test. Here's an example of one of the nested objects printed in the console:
{ type: 'claimable',
category: 'claimable',
address: '0x00000000123456lkjlkj',
symbol: 'WMATIC',
decimals: 18,
label: 'Claimable WMATIC',
img: 'networks/polygon/0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270.png',
price: 1.64,
balance: 0.03567595026086894,
balanceRaw: '35675950260868942',
balanceUSD: 0.05850855842782506 }
How do I figure out what is in the JSON data, at any depth, see if it's an array or object, and put the extracted pairs into a single array?
EDIT:
I'm trying to get this data into two rows in a google sheet. so I can track individual pieces every day. The "price", the "balanceRaw"...
The issue is the user may be participating in different AAVE pools from day to day; i.e. this JSON object might have lots of different information every day.
Here's the raw data from the API:
{"0x0000123456abcdef":{"products":[{"label":"Aave V2","assets":[{"type":"interest-bearing","category":"deposit","address":"0x27F8D03b3a2196956ED754baDc28D73be8830A6e","symbol":"DAI","decimals":18,"label":"DAI in Aave","img":"networks/polygon/0x8f3cf7ad23cd3cadbd9735aff958023239c6a063.png","protocol":"aave","protocolDisplay":"Aave","protocolSymbol":"AAVE","price":0.999254,"apy":0.027310184786005925,"balanceRaw":"2668910745526108687981","balance":2668.910745526109,"balanceUSD":2666.9197381099466},{"type":"claimable","category":"claimable","address":"0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270","symbol":"WMATIC","decimals":18,"label":"Claimable WMATIC","img":"networks/polygon/0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270.png","price":1.65,"balance":0.042818503811087726,"balanceRaw":"42818503811087730","balanceUSD":0.07065053128829474}],"meta":[]}],"meta":[{"label":"Total","value":2666.990388641235,"type":"dollar"},{"label":"Assets","value":2666.990388641235,"type":"dollar"},{"label":"Debt","value":0,"type":"dollar"}]}}
EDIT 2
I've tried the this code from this SO question:
function flattenJsonObject (parsedJson){
console.log("test first")
Object.flatten = function(parsedJson) {
var result = {};
function recurse (cur, prop) {
console.log("test")
if (Object(cur) !== cur) {
result[prop] = cur;
} else if (Array.isArray(cur)) {
for(var i=0, l=cur.length; i<l; i++)
recurse(cur[i], prop + "[" + i + "]");
if (l == 0)
result[prop] = [];
} else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop+"."+p : p);
}
if (isEmpty && prop)
result[prop] = {};
}
}
recurse(parsedJson, "");
console.log(result)
return result;
}
}
Something is not working because the second and third console.log are not being printed in the console.
Here is a demo of the flattening function. Its starting point is a string of text, which is the response from your API call:
let sourceData = '{"0x0000123456abcdef":{"products":[{"label":"Aave V2","assets":[{"type":"interest-bearing","category":"deposit","address":"0x27F8D03b3a2196956ED754baDc28D73be8830A6e","symbol":"DAI","decimals":18,"label":"DAI in Aave","img":"networks/polygon/0x8f3cf7ad23cd3cadbd9735aff958023239c6a063.png","protocol":"aave","protocolDisplay":"Aave","protocolSymbol":"AAVE","price":0.999254,"apy":0.027310184786005925,"balanceRaw":"2668910745526108687981","balance":2668.910745526109,"balanceUSD":2666.9197381099466},{"type":"claimable","category":"claimable","address":"0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270","symbol":"WMATIC","decimals":18,"label":"Claimable WMATIC","img":"networks/polygon/0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270.png","price":1.65,"balance":0.042818503811087726,"balanceRaw":"42818503811087730","balanceUSD":0.07065053128829474}],"meta":[]}],"meta":[{"label":"Total","value":2666.990388641235,"type":"dollar"},{"label":"Assets","value":2666.990388641235,"type":"dollar"},{"label":"Debt","value":0,"type":"dollar"}]}}';
JSON.flatten = function(data) {
var result = {};
function recurse (cur, prop) {
if (Object(cur) !== cur) {
result[prop] = cur;
} else if (Array.isArray(cur)) {
for(var i=0, l=cur.length; i<l; i++)
recurse(cur[i], prop ? prop+"."+i : ""+i);
if (l == 0)
result[prop] = [];
} else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop+"."+p : p);
}
if (isEmpty)
result[prop] = {};
}
}
recurse(data, "");
return result;
}
let targetData = JSON.flatten(JSON.parse(sourceData));
console.log(targetData);
The result of running this snippet is data (JS objects) like the following (summarised for brevity):
"0x0000123456abcdef.products.0.assets.0.type": "interest-bearing"
"0x0000123456abcdef.products.0.assets.0.symbol": "DAI"
"0x0000123456abcdef.products.0.assets.0.price": 0.999254
"0x0000123456abcdef.products.0.assets.0.balanceRaw": "2668910745526108687981"
and:
"0x0000123456abcdef.products.0.assets.1.type": "claimable"
"0x0000123456abcdef.products.0.assets.1.symbol": "WMATIC"
"0x0000123456abcdef.products.0.assets.1.price": 1.65
"0x0000123456abcdef.products.0.assets.1.balanceRaw": "42818503811087730"
You can see how the flattening process has built names which show you where in the hierarchy the data came from.
This may not be what you want (they are somewhat cumbersome). So, additional manipulation may be needed to format the names strings into something more friendly.
Here is what I use to get all pairs :
let result = [];
function getData(jsonString){
var data = JSON.parse(jsonString)
getAllPairs(eval(data),'data')
return result
}
function getAllPairs(obj,id) {
const regex = new RegExp('[^0-9]+');
for (let p in obj) {
var newid = (regex.test(p)) ? id + '.' + p : id + '[' + p + ']';
if (obj[p]!=null){
if (typeof obj[p] != 'object' && typeof obj[p] != 'function'){
result.push([p, obj[p]]);
}
if (typeof obj[p] == 'object') {
getAllPairs( obj[p], newid );
}
}
}
}
https://docs.google.com/spreadsheets/d/1_didPNsEswVHWygI3DNL3KGlRJgsmxYNqpOJNFLN-xI/copy
I have an an array which is mentioned below. I would like to remove an item from the array which has empty property value using JavaScript.
Actual array:
[
{
"href":"/client",
"methods":[]
},
{
"href":"/home",
"methods":
{
"type1":"GET",
"type2":"POST",
}
},
{
"href":"/about",
"methods":[]
},
{
"href":"/contact",
"methods":
{
"type1":"GET",
"type2":"POST",
}
}
]
Expecting result:
[
{
"href":"/home",
"methods":
{
"type1":"GET",
"type2":"POST",
}
},
{
"href":"/contact",
"methods":
{
"type1":"GET",
"type2":"POST",
}
}
]
This is the job for filter. however filter does not modify the existing array so you need to assign it to a different array/overwrite the current variable
a = a.filter(item => Object.keys(item.methods).length > 0)
Iterate over the object array and filter based on methods property length.
var obj = [...];
obj = obj.filter((val) => val.methods && val.methods.length !== 0);
In the case of methods, you can easily walk the object and then call delete on the keys with values that are empty.... or empty arrays. I expanded the answer to cover not only keys of methods where an array is empty, but all keys with what i would define as empty contents.
var l = [];
for (var i = 0; i < l.length; i++){
var keys = Object.keys(l[i]);
for ( var j = 0; j < keys.length; j++){
var value = keys[j];
// In your use case, you are only doing arrays so i coded it as such.
if (value.length == 0){
delete l[i][j];
}
}
}
If you want to expand it to cover a variety of cases such as empty string, empty arrays, empty maps, or null values you can defined a function to do that.
function isValueDeletable(value){
if (value == null) return true;
if (value == "") return true;
if (value instanceof Array && value.length == 0) return true;
if (value instanceof Map && Object.keys(value).length == 0) return true;
return false;
}
and apply that instead of the value.length == 0;
if (isValueDeletable(value)){ delete l[i][j]; }
Then l is modified to remove all keys with empty values.
enter var json = {};
var key = "giveitakeyvalue";
json[key] = null;
delete json[key];
What is the best way in JavaScript to go over array of object and check if an certain value already exist in one of the property of the array objects?
For example: I have array of objects as follow:
[{name:"team1",members:3},{name:"bestteam",members:4}]
Now I want to add a new object but I want to check that this object property "name" do not exist in the array before adding it
Try this
function checkIfNameExists(arr, newName) {
return arr.some(function(e) {
return e.name === newName;
});
}
where arr is your array and newName is the name to check.
You could also make it less ad hoc by passing the property to compare as a parameter, like so
function checkIfNameExists(arr, prop, newVal) {
return arr.some(function(e) {
return e[prop] ? e[prop] === newVal : false;
});
}
assuming you say an object equals an object if and only if they have the exact same elements and values for each element:
var map = {};
for (var i = 0; i < arr.length; i++) {
var index = JSON.stringify(arr[i]);
if (!map[index]) {
map[index] = 1;
} else {
map[index]++;
}
}
for (var key in map) {
if (map[key] > 1) {
console.log ('duplicate found for:');
console.log (JSON.parse (key));
}
}
simply define an array "arr" with some objects and check it out
You can iterate over the array and iterate over the keys of the array and check if some properties have the wanted value.
var data = [{ prop1: 'one', prop2: 'two' }, { prop3: 'three', prop4: 'four' }];
function find(v, data) {
data.forEach(function (a, i) {
Object.keys(a).forEach(function (k) {
if (a[k] === v) {
document.write(v + ' found in [' + i + '].' + k);
}
});
});
}
find('three', data);
You can use simple loops. Here is function which should help you to find out if key value is in array.
function keyExist(value, array) {
for(var i in array) {
for(var k in array[i])
if(array[i][k] === value) {
console.log(value + ' is in array!');
return true;
}
}
return false;
}
As for you example you can change second loop
for(var k in array[i])
if(k === value) { return true; }
to
if(array[i].name === value) { return true; }
I have no time to respond but here from my previous code maybe some will optimize it...
function JSONRemoveDuplicates(JSONDATA) {
var uniqueNames = [];
$.each(JSONDATA, function (i, el) {
if (!existInJSON(uniqueNames, el) > 0) { uniqueNames.push(el); }
});
return uniqueNames;
}
function existInJSON(jsondata, el) {
var ret = 0;
$(jsondata).each(function (index, data) {
if (data.id == el.id)
ret++;
})
return ret > 0;
}
var obj = {
"M_18-24":413109,
"F_18-24":366159,
"F_25-34":265007,
"U_25-34":1214,
"U_35-44":732
}
I want to return an object with key value pairs whose keys start with either "M" or "F". So the final object would look like
var obj = {
"M_18-24":413109,
"F_18-24":366159,
"F_25-34":265007
}
I've tried things like _.filter(obj, function(v,k) { return /^[MF]/.test(k) })...
this will do the trick:
function filte_obj_FM (inp)
{
var ret = {};
for ( var k in inp)
{
if ( k[0] == "M" || k[0] == "F" )
{
ret[k] = inp[k];
}
}
return ret;
}
see console output (F12 +-> see console) here: http://jsfiddle.net/vH3ym/2/
You can try
for (var prop in obj) { console.log(prop) }
It will give you the corresponding properties, then you can add your logic like
if(prop.indexOf('M'))
This should work:
var obj = {
"M_18-24":413109,
"F_18-24":366159,
"F_25-34":265007,
"U_25-34":1214,
"U_35-44":732
}
var filtered = {}
for(var key in obj) {
if(key[0] === "M" || key[0] === "F"){
filtered[key] = obj[key]
}
}
Another version this time using reduce:
// create a new underscore function that will return the properties from an object that pass a given predicate
_.mixin({ filterProperties: function(obj, predicate){
return _.reduce(obj, function(memo, value, key){
if( predicate(key) ){
memo[key] = value;
}
return memo;
}, {});
}});
// A couple of predicates that we can use when calling the new function
function isMale(key){
return key[0] == 'M';
}
function isFemale(key){
return key[0] == 'F';
}
// and finally getting the data we want:
var males = _.filterProperties( obj, isMale );
var females = _.filterProperties( obj, isFemale );
Here's my solution:
_.filter(_.keys(obj), function(key) {
return key.match(/U/);
}).forEach(function(kv) {
delete obj[kv];
});
I think all of yours are good and I voted them up. Thanks!
I wanna set the value in a JSON using a path string like this "a.0.b" for a JSON that looks like this:
{
a: [
{
b: 'c'
}
]
}
I came up with this solution but I wonder if there is a simpler way to write this:
function setValue(path, value, json) {
var keys = path.split('.');
_.reduce(keys, function(obj, key, i) {
if (i === keys.length - 1) {
obj[key] = value;
} else {
return obj[key];
}
}, json);
}
so calling setValue('a.0.b', 'd', {a:[{b:'c'}]}) would change the json to {a:[{b:'d'}]}
Here's a solution. I benchmarked the two possible solutions and it seems looping over object and path is faster than using the reduce function. See the JSPerf tests here: http://jsperf.com/set-value-in-json-by-a-path-using-lodash-or-underscore
function setValue(path, val, obj) {
var fields = path.split('.');
var result = obj;
for (var i = 0, n = fields.length; i < n && result !== undefined; i++) {
var field = fields[i];
if (i === n - 1) {
result[field] = val;
} else {
if (typeof result[field] === 'undefined' || !_.isObject(result[field])) {
result[field] = {};
}
result = result[field];
}
}
}