Covert JS object into flat array with parent names - javascript

I have an object like this:
data:
{
connection:
{
type: 0,
connected: false
},
acceleration:
{
x: 0,
y: 0,
z: 0,
watchId: 0,
hasError: false
}
},
Converting it to flat array like this:
"connected": false
"hasError": false
"type": 0
"watchId": 0
"x": 0
"y": 0
"z": 0
is an easy task (recurrence is your friend!).
But is there any way in Javascript to get it with so called full parents, i.e. something like this:
"connection.connected": false
"acceleration.hasError": false
"connection.type": 0
"acceleration.watchId": 0
"acceleration.x": 0
"acceleration.y": 0
"acceleration.z": 0
Or am I expecting to much?

Another variant:
function flatten(o) {
var prefix = arguments[1] || "", out = arguments[2] || {}, name;
for (name in o) {
if (o.hasOwnProperty(name)) {
typeof o[name] === "object" ? flatten(o[name], prefix + name + '.', out) :
out[prefix + name] = o[name];
}
}
return out;
}
invoke like flatten(data);

There's always a way, but note that both of these are objects, neither is an array. (associative arrays in Javascript are just objects ).
function objectFlatten( o , n ) {
var p = {}
, n = n? n : ''
, merge = function(a,b){ for( k in b) a[k] = b[k]; return a;}
;
for( i in o ) {
if( o.hasOwnProperty( i ) ) {
if( Object.prototype.toString.call( o[i] ) == '[object Object]' || Object.prototype.toString.call( o[i] ) == '[object Array]')
p = merge( p , objectFlatten( o[i] , n? n + '.' + i : i ) );
else
p[i] = o[i];
}
}
return p;
}

For posterity - check out flat or a similar utility I wrote for Forms JS, Flatten.

My five cents.
For cases when you want flatten objects but not properties
In other words.
You have something like this:
var obj = {
one_a: {
second_a: {
aaa: 'aaa',
bbb: 'bbb'
},
second_b: {
qqq: 'qqq',
third_a: {
www: 'www',
eee: 'eee',
fourth_a: {
'rrr': 'rrr',
fifth: {
ttt: 'ttt'
}
},
fourth_b: {
yyy: 'yyy',
}
},
third_b: {
'uuu': 'uuu'
}
}
},
one_b: {
iii: 'iii'
}
}
And want to make nested object flat, but don't want to flat properties:
{ 'one_a second_a ': { aaa: 'aaa', bbb: 'bbb' },
'one_a second_b ': { qqq: 'qqq' },
'one_a second_b third_a ': { www: 'www', eee: 'eee' },
'one_a second_b third_a fourth_a ': { rrr: 'rrr' },
'one_a second_b third_a fourth_a fifth ': { ttt: 'ttt' },
'one_a second_b third_a fourth_b ': { yyy: 'yyy' },
'one_a second_b third_b ': { uuu: 'uuu' },
'one_b ': { iii: 'iii' } }
Code:
function flatten (obj, includePrototype, into, prefix) {
into = into || {};
prefix = prefix || "";
for (var k in obj) {
if (includePrototype || obj.hasOwnProperty(k)) {
var prop = obj[k];
if (prop && typeof prop === "object" && !(prop instanceof Date || prop instanceof RegExp)) {
flatten(prop, includePrototype, into, prefix + k + " ");
}
else {
if (into[prefix] && typeof into[prefix] === 'object') {
into[prefix][k] = prop
} else {
into[prefix] = {}
into[prefix][k] = prop
}
}
}
}
return into;
}
Based on closure's answer

Related

Replace nested field values in an object

I need to replace every time value in a nested object with a momentJS element of its value.
const input = {
data: {
sub1: {
time: 1578857603218
}
sub2: {
some: 'thing,
foo: [{
value: 123,
time: 1578857603218
}]
}
}
}
Right now my code looks very ugly, as I'm doing this manually as there are specific fields with an optional time value.
if (data && data.sub2 && data.sub2.foo && data.sub2.foo[0].time) {
data.sub2.foo[0].time = moment(data.sub2.foo[0].time).toDate()
}
To do this in a more dynamic way, I see two options:
Pass something like an array/map with all optional time fields and replace them with a loop
Is there a better way to replace my if conditions to go through all relacing time fields?
Iterate through all keys
But this would not work for nested objects.
for (var prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
// do stuff
}
}
If you know the key value pair, then what you show as already in use is exactly what you should use. It is O(1), very quick, and essentially a single line.
Making that dynamic will require O(n) where n is the number of key value pairs, and will require several lines of code.
const renderTime = input => {
if (Array.isArray(input)) {
input.forEach(el => renderTime(el));
}
if (typeof input === 'object' && !!input) {
Object.keys(input).forEach(k => {
if (k === 'time') input[k] = 'moment(input[k]).toDate()';
else renderTime(input[k]);
});
}
};
const input = {
data: {
sub1: {
time: 1578857603218
},
sub2: {
some: 'thing',
foo: [{
value: 123,
time: 1578857603218
}]
}
}
};
renderTime(input);
console.log(input);
Whenever you want to deal with nested objects with an undetermined level of depth, think of recursivity
const setTime = (object, time) => {
for (let prop in object) {
if (!Object.prototype.hasOwnProperty.call(object, prop)) {
continue;
}
if (typeof (object[prop]) === 'object') {
setTime(object[prop], time);
}
if (prop === 'time') {
object.time = time;
}
}
return object;
};
const input = {
data: {
sub1: {
time: 1578857603218
},
sub2: {
some: 'thing',
foo: [{
value: 123,
time: 1578857603218
}]
}
}
}
setTime(input, 666);
Try this one:) The secret is recursive loop
const input = {
data: {
sub1: {
time: 1578857603218
},
sub2: {
some: 'thing',
foo: [{
value: 123,
time: 1578857603218
}]
}
}
};
function changeTime(obj) {
obj && Object.keys(obj).forEach(key => {
if(key === 'time') {
obj.time = moment(obj.time); // you have to use moment
}
if(typeof obj[key] === 'object'){
changeTime(obj[key]);
}
});
}
changeTime(input);
console.log(input);
This won't handle the case where the nested fields are in an array, but it should work for nested objects
function replaceNestedValues(obj, targetVal, nextVal) {
return Object.keys(obj).reduce((acc, key) => {
const value = obj[key];
if (typeof value === 'object' && value !== null) {
acc[key] = replaceNestedValues(value, targetVal, nextVal);
} else if (value === targetVal) {
acc[key] = nextVal;
} else {
acc[key] = value;
}
return acc;
}, {});
}
Example
const data = { a: { b: 10, c: null }, d: null };
replaceNestedValues(data, null, '');
// => { a: { b: 10, c: '' }, d: '' }
You can use this code to modify your property. The property can be present in deeply nested object or within array of objects.
foo(entry: any | any[]) {
if (Array.isArray(entry)) {
entry.forEach(ent => this.foo(ent));
} else if (typeof entry === 'object' && entry !== null) {
for (let [key, value] of Object.entries(entry)) {
if (typeof value === 'object' && value !== null) {
this.foo(value);
} else if (Array.isArray(value)) {
this.foo(value);
}
if (key === "time") {
entry[key] = "changed"; // modified value
}
}
return entry;
}
}
this.foo(data); // your object

Javascript - Traverse through the Json Object and get each item hierarchical key including nested objects and arrays

I want to get the values and keys including these as to any JSON objects as a generic method to use even for the complex objects
json
{
"timezone": 5.5,
"schedule": {
"type": "daily",
"options": {
"hour": 10,
"minute": 29
}
}
want the values and keys in hierarchical schema just like these
timezone - 5.5
schedule.type - daily
schedule.type.options.hour - 10
schedule.type.options.minute - 29
Also, I used this function can get the JSON objects all objects keys and values even in the nested arrays and objects in that
function iterate(obj) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property]);
} else {
console.log(property , obj[property])
}
}
}
return obj;
}
PS - Also I
want to use this for arrays also
"dirs": [ { "watchDir": "Desktop/logs", "compressAfterDays": 50 }, { "watchDir": "Desktop/alerts", "timeMatchRegex": "(.*)(\\d{4})-(\\d{2})-(\\d{2})-(\\d{2})_(\\d{2})(.*)", }]
the output I want to be just like this
dirs[0].watchdir="Desktop/alerts"
dirs[1].watchDir="Desktop/logs"
const obj = { "timezone": 5.5, "dirs": [ { "watchDir": "Desktop/logs", "compressAfterDays": 50 }, { "watchDir": "Desktop/alerts", "timeMatchRegex": "(.*)(\\d{4})-(\\d{2})-(\\d{2})-(\\d{2})_(\\d{2})(.*)", }] ,"schedule": { "type": "daily", "options": { "hour": 10, "minute": 29 }, 'available': true } };
function iterate(obj, str) {
let prev = '';
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
const s = isArray(obj) ? prev + str + '[' + property + ']' + '.' : prev + property + (isArray(obj[property]) ? '' : '.');
iterate(obj[property], s);
} else {
prev = (str != undefined ? str : '');
console.log(prev + property, '- ' + obj[property]);
}
}
}
return obj;
}
function isArray(o) {
return o instanceof Array;
}
iterate(obj);
Pass on the keys in the recursive call:
function iterate(obj, path = []) {
for (let property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property], [...path, property]);
} else {
console.log(path, property , obj[property])
}
}
}
}

Sanitizing all string values in a complex object?

I have a sanitizeStr() function that I need to run on EVERY property/subproperty that exists in an object like the one below:
const data = {
info: 'schools',
schools: [
{ name: 'Johnson Elementary', type: 'elementary' },
{ name: 'Iselin Middle School', type: 'middle' }
],
bestStudent: {
name: 'John',
grade: 'sixth'
}
};
The issue is that for every single one of these properties, they may or may not exist. Right now, I'm having to do multiple if checks for each property and manually running the function:
// Is there a better way to do this rather than what I have here:
if (data.info) {
data.info = sanitizeStr(data.info);
}
if (data.bestStudent) {
if (data.bestStudent.name) {
data.bestStudent.name = sanitizeStr(data.bestStudent.name);
}
if (data.bestStudent.grade) {
data.bestStudent.grade = sanitizeStr(data.bestStudent.grade);
}
}
if (data.schools) {
data.schools.forEach((school, i) => {
if (school.name) {
data.schools[i].name = sanitizeStr(school.name);
}
if (school.grade) {
data.schools[i].grade = sanitizeStr(school.grade);
}
});
}
If anyone knows of a cleaner/less manual way of doing this, it would be appreciated.
You could use an iterative and recursive approach for objects and call the function for non objects only.
function sanitizeStr(s) {
return '#' + s;
}
function iterAll(object) {
Object.keys(object).forEach(function (k) {
if (object[k] && typeof object[k] === 'object') {
iterAll(object[k]);
return;
}
object[k] = sanitizeStr(object[k]);
})
}
var data = { info: 'schools', schools: [{ name: 'Johnson Elementary', type: 'elementary' }, { name: 'Iselin Middle School', type: 'middle' }], bestStudent: { name: 'John', grade: 'sixth' } };
iterAll(data);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You must me looking for this
const sanitizeObject = (obj, callBack, isClone = false) => {
let tempObj = obj;
if(typeof callBack === 'function' && (typeof tempObj === 'string' || typeof tempObj === 'number')){
return callBack(tempObj)
}else if(typeof tempObj === 'object' && tempObj !== null){
tempObj = isClone ? (Array.isArray(tempObj) ? [...tempObj] : {...tempObj}) : tempObj;
Object.keys(tempObj).forEach(objKey => {
const valueOfobject = tempObj[objKey]
if(typeof valueOfobject === 'string' || typeof valueOfobject === 'number'){
tempObj[objKey] = callBack(tempObj[objKey])
}else {
tempObj[objKey] = sanitizeObject(valueOfobject, callBack, isClone)
}
})
}
return tempObj;
}
const data = {
test1: {
test2: [{
property: "any string",
property2: null
}]}
}
console.log(sanitizeObject(data, function (stringValue){
return stringValue + " apend"
}))

Javascript/Jquery JSON object to array inside object

I see many topics on this site, but every one deal with single Array.
My need is to convert every object with number as key to array.
For exemple,
I have an object like :
{
"parent":{
"0":{
"child":false
},
"1":{
"child":false
},
"4": {
"child":false
}
}
}
And i would like
{
"parent": [
{
"child":false
},
{
"child":false
},
null,
null,
{
"child":false
}
]
}
This is an exemple, my object can be really deep and content many object like this, so i need a generic function.
UPDATE
My try sor far using code of #Nenad Vracar :
function recursiveIteration(object) {
var newob = {};
for (var property in object) {
if (object.hasOwnProperty(property)) {
if (typeof object[property] == "object"){
var result = {};
var keys = Object.keys(object[property]);
if ($.isNumeric(keys[0])) {
console.log("======> "+property+" is table");
for (var i = 0; i <= keys[keys.length - 1]; i++) {
if (keys.indexOf(i.toString()) != -1) {
result[property] = (result[property] || []).concat(object[property][i]);
} else {
result[property] = (result[property] || []).concat(null);
}
}
newob[property] = result;
recursiveIteration(object[property]);
}
newob[property] = object[property];
recursiveIteration(object[property]);
}else{
newob[property] = object[property];
}
}
}
return newob;
}
And the JSFiddle for live try
Thanks you guys !
I think this is what you want:
var data = {
"parent": {
"0": {
"child": false
},
"1": {
"child": false
},
"4": {
"child": false
}
}
};
var convert = function(data) {
// not an object, return value
if (data === null || typeof data !== 'object')
return data;
var indices = Object.keys(data);
// convert children
for (var i = 0; i < indices.length; i++)
data[indices[i]] = convert(data[indices[i]]);
// check if all indices are integers
var isArray = true;
for (var i = 0; i < indices.length; i++) {
if (Math.floor(indices[i]) != indices[i] || !$.isNumeric(indices[i])) {
isArray = false;
break;
}
}
// all are not integers
if (!isArray) {
return data;
}
// all are integers, convert to array
else {
var arr = [];
for (var i = 0, n = Math.max.apply(null, indices); i <= n; i++) {
if (indices.indexOf(i.toString()) === -1)
arr.push(null);
else
arr.push(data[i]);
}
return arr;
}
};
console.log( convert(data) );
Here is a working jsfiddle with the data you provided in the update.
You can do this with Object.keys() and one for loop
var data = {"parent":{"0":{"child":false},"1":{"child":false},"4":{"child":false}}}, result = {}
var keys = Object.keys(data.parent);
for (var i = 0; i <= keys[keys.length - 1]; i++) {
if (keys.indexOf(i.toString()) != -1) {
result.parent = (result.parent || []).concat(data.parent[i]);
} else {
result.parent = (result.parent || []).concat(null);
}
}
console.log(result)
You might achieve this job with a very simple recursive Object method as follows. Any valid nested object (including arrays) within an object structure will be converted into an array, in which the properties are replaced with indices and values are replaced by items.
Object.prototype.valueToItem = function(){
return Object.keys(this).map(e => typeof this[e] === "object" &&
this[e] !== null &&
!Array.isArray(this[e]) ? this[e].valueToItem()
: this[e]);
};
var o = { name: "terrible",
lastname: "godenhorn",
cars: ["red barchetta", "blue stingray"],
age: 52,
child: { name: "horrible",
lastname: "godenhorn",
cars: ["fiat 124", "tata"],
age: 24,
child:{ name: "badluck",
lastname: "godenhorn",
cars: ["lamborghini countach"],
age: 2,
child: null}}},
a = o.valueToItem();
console.log(a);
Ok modified to the OP's conditions but still generic as much as it can be.
Object.prototype.valueToItem = function(){
var keys = Object.keys(this);
return keys.reduce((p,c) => typeof this[c] === "object" &&
this[c] !== null &&
!Array.isArray(this[c]) ? keys.every(k => Number.isInteger(k*1)) ? (p[c] = this[c].valueToItem(),p)
: this[c].valueToItem()
: this
,new Array(~~Math.max(...keys)).fill(null));
};
var o = {
parent: {
0: {
child : false
},
1: {
child : false
},
4: {
child : {
0: {
child : false
},
3: {
child : false
},
5: {
child : false
}
}
}
}
};
a = o.valueToItem();
console.log(JSON.stringify(a,null,4));

Find property without knowing where it is in the tree

Have a small question:
p: {
s: {
name: 'demo'
}
},
x: {
'something': 'me'
}
}
How do I get name without knowing exactly where it is in the object tree ?
Edit: How do I get to 'me' ?
You can iterate recursively:
function findByKeyName(obj, keyName) {
for (var key in obj) {
if (key === keyName) {
return obj[key];
} else {
if (typeof obj[key] === "object" && obj[key] !== null) {
return findByKeyName(obj[key], keyName);
}
}
}
}
findByKeyName(obj, "name") //returns "demo"
Ugly but true...
var getValue = (o,p) => JSON.stringify(o).replace(new RegExp('.*?' + p + '":"([^"]*).+'),"$1"),
obj = {
p: {
s: {
name: 'demo'
}
}
};
document.write(getValue(obj,"name"));
Well despite my ugly solution works perfectly i just would like to add my version of recursive approach for the public welfare.
The following will find the first appearance of the searched property.
p = {
s: {
name: 'demo',
},
x: {
'something': 'me'
},
y: null,
z: {
'something': 'you'
}
};
var getValue = (o,v) => {
var ok = Object.keys(o),
f = false,
i = 0;
while (!f && i < ok.length) {
o[ok[i]] !== null && typeof o[ok[i]] === "object" && (f = getValue(o[ok[i]],v));
i++;
}
return o[v] || f;
};
document.write(getValue(p,"something"));
And the following will find the last appearance of the searched property
p = {
s: {
name: 'demo',
},
x: {
'something': 'me'
},
y: null,
z: {
'something': 'you'
}
};
var getValue = (o,v) => {
var ok = Object.keys(o),
f = false,
i = 0;
while (!o[v] && i < ok.length) {
o[ok[i]] !== null && typeof o[ok[i]] === "object" && (f = getValue(o[ok[i]],v));
i++;
}
return o[v] || f;
};
document.write(getValue(p,"something"));

Categories

Resources