Find object by property key deep Javascript - javascript

I have the following code:
You can go to jsbin here..
https://jsbin.com/wupukuhita/1/edit?js,console
var foundObjects = [];
var findObjectByLabel = function(obj, key) {
var foundObject = [];
for(var i in obj) {
if(typeof obj[i] === "object"){
if(typeof obj[i][key] !== "undefined"){
foundObjects.push(obj[i]);
}
findObjectByLabel(obj[i],key);
}
}
return null;
};
I am iterating recursively over an object to find out if a certain property exists.
Then if it does exist, return the parent object.
You can check the jsbin link for a full example.
I do not like the foundObjects which is outside the function.
How can i put it inside the function and just return from the function the objects that contain a certain property.
https://jsbin.com/wupukuhita/1/edit?js,console

You can use javascript closures which is basically a function inside another function and the second function can access the main function objects
see the full code here , it works the same as your except we return the array
var foundObjects = function (obj,key) {
var foundObject = [];
var findObjectByLabel = function(obj,key) {
for (var i in obj) {
if (typeof obj[i] === 'object') {
if (typeof obj[i][key] !== 'undefined') {
foundObject.push(obj[i]);
}
findObjectByLabel(obj[i], key);
}
}
return null;
};
findObjectByLabel(obj,key);
return foundObject ;
}
var mainObj = {
name: 'MainForm', // main form
type: 'Form',
dirty: false,
valid: true,
Errors: [],
formOrInputElements: [
{
name: 'Age', // normal input
type: 'Text',
value: '',
dirty: true,
valid1: true,
test: {
name: 'test',
valid1: false,
},
Errors: [],
},
{
name: 'CNP', // normal input
type: 'Text',
value: '',
dirty: true,
valid: true,
Errors: [],
},
],
};
let foundObject = foundObjects(mainObj, 'valid1');
console.log(foundObject[0]);
console.log(foundObject[1]);

Alternatively, you can use array#reduce and iterate through each of the key values in object. In case of an array, recursively invoke the function
for each object. In case of an object, invoke the function with that object.
var mainObj = { name: "MainForm", type: "Form", dirty: false, valid: true, Errors: [], formOrInputElements: [ { name: "Age", type: "Text", value: "", dirty: true, valid1: true, test: { name: "test", valid1: false }, Errors: [] }, { name: "CNP", type:"Text", value: "", dirty: true, valid: true, Errors: [] } ] }
var findObjectByLabel = function(obj, key) {
return Object.keys(obj).reduce((r, k) => {
if (k === key) {
r.push(Object.assign({}, obj));
} else if (Array.isArray(obj[k])) {
obj[k].forEach(x => r = r.concat(findObjectByLabel(x, key)));
} else if (typeof obj[k] === 'object') {
r = r.concat(findObjectByLabel(obj[k], key));
}
return r;
}, []);
};
console.log(findObjectByLabel(mainObj, "valid1"));
.as-console-wrapper { max-height: 100% !important; top: 0; }

function findObjectByLabel(haystack, needle, buffer = []) {
if (typeof haystack === 'object') {
for (const prop in haystack) {
const result = prop === needle ? [haystack] : findObjectByLabel(haystack[prop], needle);
if (result.length > 0) {
buffer = buffer.concat(result);
}
}
}
return buffer;
}
// Unit test
function test_findObjectByLabel() {
const obj = {
foo: {
foo1: {
item1: 'item1',
item2: 'item2',
item3: 'item3',
},
foo2: {
item1: 'item1',
item2: 'item2',
item3: 'item3',
subFoo: {
item1: 'item1',
item2: 'item2',
needle: 'needle',
}
}
},
bar: {
bar1: {
item1: 'item1',
item2: 'item2',
item3: 'item3',
},
bar2: {
item1: 'item1',
item2: 'item2',
item3: 'item3',
needle: 'needle',
}
},
}
const expected = [
obj.foo.foo2.subFoo, // <-- contain "needle"
obj.bar.bar2, // <-- contain "needle"
];
const actual = findObjectByLabel(obj, 'needle');
if (JSON.stringify(actual) === JSON.stringify(expected)) {
console.log('Pass');
console.log('expected => ', JSON.stringify(expected, null, 4));
console.log('actual => ', JSON.stringify(actual, null, 4));
} else {
console.log('Fail');
console.log('Actual')
console.log(JSON.stringify(actual));
console.log('is not equal to expected');
console.log(JSON.stringify(expected));
}
}
test_findObjectByLabel();

Related

delete part of an object and place it in another object

I have an object (values). I want to select the field which startWith(')and which contains an object(for this case the 2). I want to delete the entire object from the createValue array and place it in an object CreateFileValue. You can check the output
Example input:
const values = {
ID: ,
c: [{
f: "",
v: 'hello',
},
{
f: "102",
fi: "doc.pdf",
v: {
f: 'F2',
fi: '',
v: 'jkhkjhkhkjh'
},
}
]
}
Example output:
const values = {
ID:817,
c: [{
f: "F",
v: 'hello',
}
],
c: {
"f": "F",
"fi": "ff.pdf",
"v": "jkhkjhkhkjh"
}
}
const values = {
ID: 7,
c: [{
f: "FL",
v: 'hello',
},
{
f: "F2",
fi: "doc.pdf",
v: {
f: '',
fi: '.pdf',
v: 'jkhkjhkhkjh'
},
}
]
}
const res = () => {
if (values.calues.startsWith('') && typeof values.cralues.startsWith('F') === 'object') {
return {
};
}
}
};
console.log(res());
const values = {
ID: 748817,
createValues: [{
field: "FLD_STR_101",
value: 'hello',
},
{
field: "FLD_STR_102",
fileName: "doc.pdf",
value: {
field: 'FLD_STR_102',
fileName: 'bulletin_paie_avril.pdf',
value: 'jkhkjhkhkjh'
},
}
]
}
// build createFileValues structure
values.createFileValues = values.createValues.filter(v => v.field.startsWith("FLD_STR_") && typeof v.value === "object");
// filter the files out of createValues
values.createValues = values.createValues.filter(v => !values.createFileValues.includes(v));
console.log(values);
values.createValues.startsWith('FLD_STR_') isn't valid because createValues is an array. You'll need to iterate over values.createValues in order to check each field.startsWith('FLD_STR_') and if value is an object (see How to detect if a variable is a pure javascript object for an example of this).
const values = {
ID: 748817,
createValues: [{
field: "FLD_STR_101",
value: 'hello',
},
{
field: "FLD_STR_102",
fileName: "doc.pdf",
value: {
field: 'FLD_STR_102',
fileName: 'bulletin_paie_avril.pdf',
value: 'jkhkjhkhkjh'
}
}
]
}
const res = () => {
var newResult = {
ID: values.ID,
createValues: []
};
values.createValues.forEach(function(item) {
var newCreateValue = {};
if (item.field.startsWith('FLD_STR_') && item.value.constructor.name === "Object") {
newCreateValue.field = item.value.field;
newCreateValue.value = item.value.value;
newCreateValue.fileName = item.value.fileName;
// if this is a createFilesValue, add it as a new key/value to the newResult object
newResult.createFileValues = newCreateValue;
} else {
newCreateValue.field = item.field;
newCreateValue.value = item.value;
// if this is an existing createValues, add it to the createValues array
newResult.createValues.push(newCreateValue);
}
});
return newResult;
};
console.log(res());
This should work
const values = {
ID: 748817,
createValues: [{
field: "FLD_STR_101",
value: 'hello',
},
{
field: "FLD_STR_102",
fileName: "doc.pdf",
value: {
field: 'FLD_STR_102',
fileName: 'bulletin_paie_avril.pdf',
value: 'jkhkjhkhkjh'
},
}
]
}
const res = (data) => {
let oldCreateValues = data.createValues;
let oldCreateFileValues = data.createFileValues;
let newCreateValues = [];
let newCreateFileValues = oldCreateFileValues && Array.isArray(oldCreateFileValues) ? oldCreateFileValues : [];
if (oldCreateValues && Array.isArray(oldCreateValues)) {
oldCreateValues.forEach(item => {
if (item.field.startsWith('FLD_STR_') && item.value && typeof item.value === 'object') {
newCreateFileValues.push(item);
} else {
newCreateValues.push(item);
}
});
}
data.createValues = newCreateValues;
data.createFileValues = newCreateFileValues;
return data;
};
console.log(res(values));

Can we assign an object key to another variable like this? [duplicate]

Given a JavaScript object, how can I convert it into an array of objects (each with key, value)?
Example:
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' }
resulting like:
[
{ key: 'firstName', value: 'John' },
{ key: 'lastName', value: 'Doe' },
{ key: 'email', value: 'john.doe#gmail.com' }
]
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' }
var output = Object.entries(data).map(([key, value]) => ({key,value}));
console.log(output);
Inspired By this post
Using map function
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' };
var result = Object.keys(data).map(key => ({ key, value: data[key] }));
console.log(result);
You can just iterate over the object's properties and create a new object for each of them.
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' };
var result = [];
for(var key in data)
{
if(data.hasOwnProperty(key))
{
result.push({
key: key,
value: data[key]
});
}
}
The previous answer lead me to think there is a better way...
Object.keys(data).map(function(key) {
return { key, value: data[key] };
});
or in ES6 using arrow functions:
Object.keys(data).map((key) => ({ key, value: data[key] }));
Just make your life easier and use es6 syntax with a map
var output = Object.keys(data).map(key => {
return {
key: key,
value: data[key]
};
})
var result = [];
for(var k in data) result.push({key:k,value:data[k]});
Or go wild and make the key and value keys customizable:
module.exports = function objectToKeyValueArray(obj, keyName = 'key', valueName = 'value') {
return Object
.keys(obj)
.filter(key => Object.hasOwnProperty.call(obj, key))
.map(key => {
const keyValue = {};
keyValue[keyName] = key;
keyValue[valueName] = obj[key];
return keyValue;
});
};
An alternative method for doing this that works on multi level objects and does not use recursion.
var output = []
var o = {
x:0,
y:1,
z:{
x0:{
x1:4,
y1:5,
z1:6
},
y0:2,
z0:[0,1,2],
}
}
var defer = [ [ o ,[ '_root_' ] ] ]
var _defer = []
while(defer.length){
var current = defer.pop()
var root = current[1]
current = current[0]
for(var key in current ){
var path = root.slice()
path.push(key)
switch( current[key].toString() ){
case '[object Object]':
_defer.push( [ current[key] , path ] )
break;;
default:
output.push({
path : path ,
value : current[key]
})
break;;
}
}
if(!defer.length)
defer = _defer.splice(0,_defer.length)
}
[
{ path: [ '_root_', 'x' ], value: 0 },
{ path: [ '_root_', 'y' ], value: 1 },
{ path: [ '_root_', 'z', 'y0' ], value: 2 },
{ path: [ '_root_', 'z', 'z0' ], value: [ 0, 1, 2 ] },
{ path: [ '_root_', 'z', 'x0', 'x1' ], value: 4 },
{ path: [ '_root_', 'z', 'x0', 'y1' ], value: 5 },
{ path: [ '_root_', 'z', 'x0', 'z1' ], value: 6 }
]
const array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
const obj = Object.fromEntries(array.map(item => [item.key, item.value]));
console.log(obj);
I would say to use npm package flat.
works amazing for nested objects and arrays.
var flatten = require('flat')
flatten({
key1: {
keyA: 'valueI'
},
key2: {
keyB: 'valueII'
},
key3: { a: { b: { c: 2 } } }
})
// {
// 'key1.keyA': 'valueI',
// 'key2.keyB': 'valueII',
// 'key3.a.b.c': 2
// }
const array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
const obj = Object.fromEntries(array.map(item => [item.key, item.value]));
console.log(obj);

Change all null to '' in array of Objects (javascript)

I have this array of objects shown below
Object {Results:Array[3]}
Results:Array[3]
[0-2]
0:Object
id=null
name: "Rick"
Value: "34343"
1:Object
id=2
name: null
Value: "2332"
2:Object
id=2
name:'mike'
Value: null
As you can see, in 1 object i have id as null, 2nd object has name as null and 3rd object has Value as null. Each object has some property as null.
I want to loop through all of these and replace null with ''. Can someone let me know how to achieve this...
Here's something quick:
var results = [{
id: null,
name: "Rick",
Value: "34343"
}, {
id: 2,
name: null,
Value: "2332"
}, {
id: 2,
name: 'mike',
Value: null
}];
results.forEach(function(object){
for(key in object) {
if(object[key] == null)
object[key] = '';
}
});
console.log(JSON.stringify(results, null, 2))
You only needed to Google looping through objects. Here's an example:
Looping through every key (if your keys are unknown or you want to do the same for all keys)
for (const obj of arr) {
if (typeof obj !=== 'object') continue;
for (k in obj) {
if (!obj.hasOwnProperty(k)) continue;
v = obj[k];
if (v === null || v === undefined) {
obj[k] = '';
}
}
}
If your keys are known:
for (const obj of arr) {
if (obj.name === undefined || obj.name === null) {
obj.name = '';
}
}
You could iterate over the elements of the array and over the keys and assign the wanted strings if necessary.
var data = { Results: [{ id: null, name: "Rick", Value: "34343" }, { id: 2, name: null, Value: "2332" }, { id: 2, name: 'mike', Value: null }] };
data.Results.forEach(function (o) {
Object.keys(o).forEach(function (k) {
if (o[k] === null) {
o[k] = '';
}
});
});
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can look over your array and all keys and if the property contains null replace it with empty string, e.g.:
var arr = [
{ id=null name: "Rick" Value: "34343" },
{ id=2 name: null Value: "2332" },
{ id=2 name:'mike' Value: null }
];
arr.forEach(function(o) {
Object.keys(o).forEach(function(key) {
id (o[key] === null) {
o[key] = '';
}
});
});

Merge arrays in JS

Suppose I have the following arrays:
var first = [
{ id: 1, name: 'first' },
{ id: 2, name: 'second' },
{ id: 3, name: 'third' }
]
var second = [
{ id: 2, field: 'foo2' },
{ id: 3, field: 'foo3' },
{ id: 4, field: 'foo4' }
]
var third = [
{ id: 2, data: 'some2' },
{ id: 5, data: 'some5' },
{ id: 6, data: 'some6' }
]
I want to merge them to get the following result:
var result = [
{ id: 1, name: 'first', field: undefined, data: undefined },
{ id: 2, name: 'second', field: 'foo2', data: 'some2' },
{ id: 3, name: 'third', field: 'foo3', data: undefined },
{ id: 4, name: undefined, field: 'foo4', data: undefined },
{ id: 5, name: undefined, field: undefined, data: 'some5' },
{ id: 6, name: undefined, field: undefined, data: 'some6' }
]
How could I do it with JavaScript?
You should get all existed keys and after create new Objects with fill "empty" keys:
function mergeArrays(){
var keys = {};
//save all existed keys
for(var i=arguments.length;--i;){
for(var j=arguments[i].length;--j;){
for(var key in arguments[i][j]){
keys[key] = true;
}
}
}
var res = [];
for(var i=arguments.length;--i;){
for(var j=arguments[i].length;--j;){
//set clone of object
var clone = JSON.parse(JSON.stringify(arguments[i][j]));
for(var key in keys){
if(!(key in clone)){
clone[key] = undefined;
}
}
res.push(clone);
}
}
return res;
}
https://jsfiddle.net/x3b0tk3g/
There is no simple solution for what you want. Here is my suggestion.
var first = [
{ id: 1, name: 'first' },
{ id: 2, name: 'second' },
{ id: 3, name: 'third' }
]
var second = [
{ id: 2, filed: 'foo2' },
{ id: 3, field: 'foo3' },
{ id: 4, field: 'foo4' }
];
var third = [
{ id: 2, data: 'some2' },
{ id: 4, data: 'some4' },
{ id: 6, data: 'some6' }
];
var result = {};
first.concat(second,third).forEach(function(item){
var id = item.id;
var row = result[id];
if(!row){
result[id] = item;
return;
}
for(var column in item){
row[column] = item[column];
}
});
var finalResult = Object.keys(result).map(function(id){
return result[id];
});
console.log(finalResult);
fiddle: http://jsfiddle.net/bs20jvnj/2/
function getByProperty(arr, propName, propValue) {
for (var i = 0; i < arr.length; i++) {
if (arr[i][propName] == propValue) return arr[i];
}
}
var limit = first.length + second.length + third.length;
var res = [];
for (var i = 1; i < limit; i++) {
var x = $.extend({}, getByProperty(first, "id", i), getByProperty(second, "id", i), getByProperty(third, "id", i));
console.log(x["id"]);
if (x["id"] === undefined) x["id"] = i;
res.push(x);
}
console.log(res);
There's probably a shorter way to solve this, but this covers all the steps, including ensuring that there are default properties that are undefined if not found. It also takes any number of input arrays, and you can specify what default keys you require if they're not already covered by the keys in the existing objects, so pretty future-proof for your needs.
// merges the key/values of two objects
function merge(a, b) {
var key;
if (a && b) {
for (key in b) {
if (b.hasOwnProperty(key)) {
a[key] = b[key];
}
}
}
return a;
}
function concatenate() {
var result = [];
var args = arguments[0];
for (var i = 0, l = args.length; i < l; i++) {
result = result.concat(args[i]);
}
return result;
}
// return a default object
function getDefault() {
return {
id: undefined,
name: undefined,
data: undefined,
field: undefined
};
}
// loop over the array and check the id. Add the id as a key to
// a temporary pre-filled default object if the key
// doesn't exist, otherwise merge the existing object and the
// new object
function createMergedArray(result) {
var temp = {};
var out = [];
for (var i = 0, l = result.length; i < l; i++) {
var id = result[i].id;
if (!temp[id]) temp[id] = getDefault();
merge(temp[id], result[i]);
}
// loop over the temporary object pushing the values
// into an output array, and return the array
for (var p in temp) {
out.push(temp[p]);
}
return out;
}
function mergeAll() {
// first concatenate the objects into a single array
// and then return the results of merging that array
return createMergedArray(concatenate(arguments));
}
mergeAll(first, second, third);
DEMO

How do I recursively search an object tree and return the matching object based on a key/value using JavaScript/Prototype 1.7

I've got some nested object data and I want to search it and return the matching object based on the id.
var data = [{id: 0, name: 'Template 0', subComponents:[
{id: 1, name: 'Template 1', subItems:[
{id: 2, name: 'Template 2', subComponents:[{id: 3, name: 'Template 3'}], subItems: [{id: 4, name: 'Template 4'}]}
]}
]}
];
So I want to do something like this
getObjectByKeyValue({id: 3})
and have it return
{id: 3, name: 'Template 3'}
It's sort of got to be done generically because I have subItems, AND subComponents which could each have children.
I tried this using Prototype 1.7 and no luck - I think this just searches an array, and not a tree with it's sub nodes:
data.find(function(s){return s.id == 4;})
Thanks in advance!!!!!!
I went a slightly different route and made the findKey method an Object protype:
Object.prototype.findKey = function(keyObj) {
var p, key, val, tRet;
for (p in keyObj) {
if (keyObj.hasOwnProperty(p)) {
key = p;
val = keyObj[p];
}
}
for (p in this) {
if (p == key) {
if (this[p] == val) {
return this;
}
} else if (this[p] instanceof Object) {
if (this.hasOwnProperty(p)) {
tRet = this[p].findKey(keyObj);
if (tRet) { return tRet; }
}
}
}
return false;
};
Which you would call directly on the data object, passing in the key/value you're looking for:
data.findKey({ id: 3 });
Note that this function allows you to find an object based on any key:
data.findKey({ name: 'Template 0' });
See example → (open console to view result)
Not the best of the and final solution.
But can get you a start for what you are looking...
var data = [{id: 0, name: 'Template 0', subComponents:[
{id: 1, name: 'Template 1', subItems:[
{id: 2, name: 'Template 2', subComponents:[{id: 3, name: 'Template 3'}], subItems: [{id: 4, name: 'Template 4'}]}
]}
]}
];
function returnObject(data,key,parent){
for(var v in data){
var d = data[v];
if(d==key){
return parent[0];
}
if(d instanceof Object){
return returnObject(d,key,data);
};
}
}
function returnObjectWrapper(datavar,key){
return returnObject(datavar,key.id)
}
returnObjectWrapper(data,{id:3})
Please see my solution below or http://jsfiddle.net/8Y6zq/:
var findByKey = function (obj, key) {
var j, key = key || '', obj = obj || {}, keys = key.split("."),
sObj = [], ssObj = [], isSelector = !!(keys.length > 0);
var findKey = function (obj, key) {
var k;
for (k in obj) {
if (k === key) {
sObj.push(obj[k]);
} else if (typeof obj[k] == 'object') {
findKey(obj[k], key);
}
}
};
if (isSelector) {
var nKey = keys.shift();
findKey(obj, nKey);
while (keys.length > 0) {
nKey = keys.shift();
if (sObj.length > 0) {
ssObj = sObj.slice(0), sObj = [];
for (j in ssObj) {
findKey(ssObj[j], nKey);
}
}
}
} else {
findKey(obj, key);
}
// return occurrences of key in array
return (sObj.length === 1) ? sObj.pop() : sObj;
};
var data = [
{id: 0, name: 'Template 0', subComponents: [
{id: 1, name: 'Template 1', subItems: [
{id: 2, name: 'Template 2', subComponents: [
{id: 3, name: 'Template 3'}
], subItems: [
{id: 4, name: 'Template 4'}
]}
]}
]},
{subComponents:{
comp1:'comp1 value',
comp2:'comp2 value',
}}
];
alert(JSON.stringify(findByKey(data, 'subComponents')));
alert(JSON.stringify(findByKey(data, 'subComponents.comp1')));
alert(JSON.stringify(findByKey(data, 'subComponents.comp2')));
In this implementation we can use search by KEY or SELECTOR (eg. "<paren_key>.<child_key_1>.<child_key_2>. ... <child_key_N>")
In case you really need a search through your tree data return all results (not a unique key), here is a little modified version of mVChr's answer:
Object.prototype.findKey = function (keyObj) {
var p, key, val, tRet;
var arr = [];
for (p in keyObj) {
if (keyObj.hasOwnProperty(p)) {
key = p;
val = keyObj[p];
}
}
for (p in this) {
if (p == key) {
if (this[p] == val) {
arr.push(this);
}
} else if (this[p] instanceof Object) {
if (this.hasOwnProperty(p)) {
tRet = this[p].findKey(keyObj);
if (tRet) {
for (var i = 0; i < tRet.length; i++)
arr.push(tRet[i]);
}
}
}
}
if (arr.length > 0)
return arr;
else
return false;
};
We now use object-scan for data processing tasks like this. It's pretty powerful once you wrap your head around how to use it. Here is how you'd answer your questions
// const objectScan = require('object-scan');
const find = (id, input) => objectScan(['**'], {
abort: true,
rtn: 'value',
filterFn: ({ value }) => value.id === id
})(input);
const data = [{ id: 0, name: 'Template 0', subComponents: [{ id: 1, name: 'Template 1', subItems: [{ id: 2, name: 'Template 2', subComponents: [{ id: 3, name: 'Template 3' }], subItems: [{ id: 4, name: 'Template 4' }] }] }] }];
console.log(find(3, data));
// => { id: 3, name: 'Template 3' }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan

Categories