Find property without knowing where it is in the tree - javascript

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

Related

Javascript assign only intersection of source to target object

I have two object, and I want to update the target object values from source, but only the values which exists in the target already.
const target = {
a: 1,
b: 2,
c: {
c_a: 3,
c_b: 4,
}
}
const source = {
a: 10,
c: {
c_a: 30,
c_d: 50,
},
d: 60
}
const result = assignOnlyIntersection(target, source);
JSON.stringify(result)
// {
// a: 10,
// b: 2,
// c: {
// c_a: 30,
// c_b: 4
// }
// }
Is there a good way in ES6 for this? If not, does lodash has a feature for this? If not how can it be solved in a nice way?
A simple recursive function will do:
function assignOnlyIntersection(target, source) {
if (Object(target) !== target || Object(source) !== source)
return source;
for (const p in source)
if (p in target)
target[p] = assignOnlyIntersection(target[p], source[p]);
return target;
}
You need to iterate through keys of target object, check if there is same key on source object and if so, replace the value. You also need recursion if you find an object on the way.
Something like this could do the job:
const target = { a: 1, b: 2, c: { c_a: 3, c_b: 4 }};
const source = { a: 10, c: { c_a: 30, c_d: 50 }, d: 60 };
function assignOnlyIntersection(t, s) {
Object
.keys(s)
.filter(k => k in t)
.forEach(k => {
if (typeof t[k] === 'object') {
assignOnlyIntersection(t[k], s[k]);
} else {
t[k] = s[k];
}
});
return t;
}
const result = assignOnlyIntersection(target, source);
console.log(JSON.stringify(result));
Also note that if you want to support replacing with falsy values like 0 or '', you will need to check for existence with typeof t[k] !== 'undefined'.
As #ghybs noted, if you want to also support replacing with undefined values, you will need to check via in operator k in t instead.
You could take a recursive approach by checking for objects.
function deepAssign(target, source) {
Object.keys(source).forEach(function (k) {
var objectCount = [source, target]
.map(o => o[k])
.map(o => o && typeof o === 'object')
.map(Boolean)
.reduce((a, b) => a + b)
if (!(k in target) || objectCount === 1) {
return;
}
if (objectCount === 2) {
return deepAssign(target[k], source[k]);
}
target[k] = source[k];
});
return target;
}
var source = { a: 10, c: { c_a: 30, c_d: 50, }, d: 60 },
target = { a: 1, b: 2, c: { c_a: 3, c_b: 4, } };
console.log(deepAssign(target, source));
.as-console-wrapper { max-height: 100% !important; top: 0; }
another ES6 approach
non-mutative:
const intersector = (target = {}, source = {}) => {
return Object.keys(source).reduce((acc, prop) => {
if (target.hasOwnProperty(prop)) {
if (typeof source[prop] === 'object' && source[prop] !== null) {
acc[prop] = intersector(target[prop], source[prop]);
}
else {
acc[prop] = source[prop];
}
}
return acc;
}, {});
}
mutative:
const intersector = (target = {}, source = {}) => {
return Object.keys(source).reduce((acc, prop) => {
if (target.hasOwnProperty(prop)) {
if (typeof source[prop] === 'object' && source[prop] !== null) {
acc[prop] = intersector(target[prop], source[prop]);
}
else {
acc[prop] = source[prop];
}
target[prop] = acc[prop];
}
return acc;
}, target);
};
You can combine Array.reduce() and Object.keys() ES6 features to achieve this:
let target = {
a: 1,
b: 2,
c: {
c_a: 3,
c_b: 4,
}
}
let source = {
a: 10,
c: {
c_a: 30,
c_d: 50,
},
d: 60
}
const assignOnlyIntersection = (sourceObj, targetObj) => {
return Object.keys(targetObj).reduce((intersection, key) => {
intersection[key] = (typeof targetObj[key] === 'object') ? assignOnlyIntersection(sourceObj[key], targetObj[key]) : sourceObj[key] || targetObj[key];
return intersection;
}, {})
}
target = assignOnlyIntersection(source, target);
console.log(target);

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"
}))

Sorting trees recursively

I want to sort children for each root inside my object tree - how can I do this?
The tree:
{
folder: { id: 1, name: 'root' },
children: [
{
folder: { id: 2, parentId: 1, name: 'zzz' },
children: []
},
{
element: { id: 1, name: 'aaa' },
children: []
}
]
}
Sorting it would swap folder and element here, etc. The actual tree is much bigger, with much higher depth. How can I do this?
I have an algorithm finding something in this tree:
/**
* searchFor {
* type: '',
* index: '',
* value: ''
* }
*/
var search = function (data, searchFor) {
if (data[searchFor.type] != undefined &&
data[searchFor.type][searchFor.index] == searchFor.value) {
return data;
} else if (data.children != null) {
var result = null;
for (var i = 0; result == null && i < data.children.length; i++) {
result = search(data.children[i], searchFor);
}
return result;
}
return null;
};
But I honestly have no idea how can I just sort it. How should I do this?
I have tried something like this, but it doesn't work:
/**
* sortBy {
* type: '',
* index: '',
* order: '' // asc/desc
* }
*/
var sort = function (data, sortBy) {
if (data.children != null) {
// sort all children here, but how?
var result = null;
for (var i = 0; result == null && i < data.children.length; i++) {
result = search(data.children[i], sortBy);
}
return result;
}
return null;
}
This should work.
function sortTree(tree){
tree.children.sort(function(a,b){
if (a.folder !== undefined && b.folder === undefined) return -1;
if (a.folder === undefined && b.folder !== undefined) return 1;
a = a.folder === undefined ? a.element;
b = b.folder === undefined ? b.element;
if (a.name == b.name) return 0;
return a.name < b.name ? -1 : 1;
});
for (i = 0; i < tree.children.length){
sortTree(tree.children[i])
}
}

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

Covert JS object into flat array with parent names

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

Categories

Resources