How to search through multi layers nested objects by keys in js - javascript

I would like to search through multi layered nested object in javascript. The number of layers is dynamic.Not fixed.
Let's say I have an object like this.
data={
"_index": "sample_data_logs",
"_type": "_doc",
"_id": "lDMTgnEBbYNxp5GHN-gj",
"_version": 1,
"_score": 7.6343846,
"_source": {
"agent": "Mozilla/4.0",
"bytes": 6948,
"clientip": "172.3.128.226",
"extension": "",
"geo": {
"srcdest": "IN:BY",
"src": "IN",
"dest": "BY",
"coordinates": {
"lat": 41.92076333,
"lon": -71.49138139
}
},
"host": "www.host.co",
"index": "sample_data_logs",
"ip": "172.3.128.226",
"machine": {
"ram": 5368709120,
"os": "win 8"
},
"memory": null,
"phpmemory": null,
"request": "/apm",
"response": 200,
"tags": [
"success",
"security"
],
"timestamp": "2020-04-13T11:05:05.551Z",
"url": "https://www.elastic.co/downloads/apm",
"utc_time": "2020-04-13T11:05:05.551Z"
}
}
keys= ["_index","bytes","os","tags"];
And I have an array of key values to find or filter in data.
How can I do that?
Using lodash I have tried
_.pick(data_, keys);
I don't get the expected results which should be:
{
"_index": "sample_data_logs",
"bytes": 6948,
"os": "win 8",
"tags": [
"success",
"security"
]
}
What is the best way of doing this? can it be done in vanilla js?

You need to traverse your data recursively, like this:
Explanation:
We have a function traverse that takes:
an object (called data),
a list of keys (called keys),
and an object that contains the current result (the found key/value pairs called result)
In the traverse function, we go over the first level keys of object data (using Object.keys(data)) and we check if each of them is in the keys list, and if it is then we add that key/value pair to the result and go to the next one.
But if it is not in the keys, then we need to check if that key is a nested object, so we do that with this conditions:
if (
data[k] &&
typeof data[k] === "object" &&
Object.keys(data[k]).length > 0
)
The first one (data[k]) is used to rule out null and undefined
The second one (typeof data[k] === "object") is used to check if the value is an object
The third condition is used to rule out native objects like Date
And if it is a nested object, then we call the traverse (recursive) again
let data = {
_index: "sample_data_logs",
_type: "_doc",
_id: "lDMTgnEBbYNxp5GHN-gj",
_version: 1,
_score: 7.6343846,
_source: {
agent: "Mozilla/4.0",
bytes: 6948,
clientip: "172.3.128.226",
extension: "",
geo: {
srcdest: "IN:BY",
src: "IN",
dest: "BY",
coordinates: {
lat: 41.92076333,
lon: -71.49138139,
},
},
host: "www.host.co",
index: "sample_data_logs",
ip: "172.3.128.226",
machine: {
ram: 5368709120,
os: "win 8",
},
memory: null,
phpmemory: null,
request: "/apm",
response: 200,
tags: ["success", "security"],
timestamp: "2020-04-13T11:05:05.551Z",
url: "https://www.elastic.co/downloads/apm",
utc_time: "2020-04-13T11:05:05.551Z",
},
};
let keys = ["_index", "bytes", "os", "tags"];
function traverse(data, keys, result = {}) {
for (let k of Object.keys(data)) {
if (keys.includes(k)) {
result = Object.assign({}, result, {
[k]: data[k]
});
continue;
}
if (
data[k] &&
typeof data[k] === "object" &&
Object.keys(data[k]).length > 0
)
result = traverse(data[k], keys, result);
}
return result;
}
result = traverse(data, keys);
console.log(result);

You can use :
const object1 = {
a: 'somestring',
b: 42
};
for (let [key, value] of Object.entries(object1)) {
if (key === target) {// do somethings}
}
If the keys is equal to your need, you can perform your treatment ?
Hope it's help

Related

How can I update this nested object's value if one of its properties match in JS?

I saw many answers, but I haven't been able to modify any to my need.
Object
{
"id": "476ky1",
"custom_id": null,
"name": "Reunião com o novo Gerente de Vendas - Airton",
"text_content": null,
"description": null,
"status": {
"id": "p3203621_11svBhbO"
},
"archived": false,
"creator": {
"id": 3184709
},
"time_spent": 0,
"custom_fields": [{
"id": "36c0de9a-9243-4259-ba57-bd590ba07fe0",
"name": "Comments",
"value": "dsdsdsds"
}],
"attachments": []
}
Within custom_fields, if the property name's value is Comments, update the value property.
I've tried it like this, using this approach, for example, but it doesn't produce the expected result.
const updatedComment = [{ name: "Comments", value: "The comment is updated"}];
updateNestedObj(taskData, updatedComment)
function updateNestedObj(obj, updates) {
const updateToApply = updates.find(upd => upd.id === obj.id);
if (updateToApply) {
obj.title = updateToApply.content;
obj.note = updateToApply.note;
}
// Apply updates to any child objects
for(let k in obj) {
if (typeof(obj[k]) === 'object') {
updateNestedObj(obj[k], updates);
}
}
}
You're using the wrong property names when you search updates for updateToApply, and then when assigning the value.
When you recurse on children, you need to distinguish between arrays and ordinary objects, so you can loop over the nested arrays. You also have to skip null properties, because typeof null == 'object'.
const updatedComment = [{
name: "Comments",
value: "The comment is updated"
}];
function updateNestedObj(obj, updates) {
let updateToApply = updates.find(upd => upd.name == obj.name);
if (updateToApply) {
obj.value = updateToApply.value;
}
// Apply updates to any child objects
Object.values(obj).forEach(child => {
if (Array.isArray(child)) {
child.forEach(el => updateNestedObj(el, updates));
} else if (typeof(child) === 'object' && child != null) {
updateNestedObj(child, updates);
}
});
}
const taskData = {
"id": "476ky1",
"custom_id": null,
"name": "Reunião com o novo Gerente de Vendas - Airton",
"text_content": null,
"description": null,
"status": {
"id": "p3203621_11svBhbO"
},
"archived": false,
"creator": {
"id": 3184709
},
"time_spent": 0,
"custom_fields": [{
"id": "36c0de9a-9243-4259-ba57-bd590ba07fe0",
"name": "Comments",
"value": "dsdsdsds"
}],
"attachments": []
};
updateNestedObj(taskData, updatedComment)
console.log(taskData);
Try this:
const updatedComment = [{ name: "Comments", value: "A new comment value" }]
// you can add as many updates as you want
function update(obj, updates) {
for (const update in updates) {
for (const field in obj.custom_fields) {
if (obj.obj.custom_fields.name == update.name) {
obj.obj.custom_fields.value = update.value
}
}
}
}
update(obj, updatedComment)

Javascript -sort array based on another javascript object properties

I have one javascript array and one object . Need help to sort javascript object keys based on the order number in another array
In subgroup array , I have name , order number. Need to sort Offerings keys based on that order number
const subgroup = [
{
"code": "6748",
"name": "test123",
"orderNumber": "0"
},
{
"code": "1234",
"name": "customdata",
"orderNumber": "1"
}
]
const offerings = {
"customdata" : [
{
"code": "Audi",
"color": "black"
}
],
"test123" : [
{
"brand": "Audi",
"color": "black"
}
]
}
I believe this should work for you. I've added some comments in the code that should hopefully do an okay job of explaining what is happening.
var subgroup = [{
"code": "6748",
"name": "test123",
"orderNumber": "0"
}, {
"code": "1234",
"name": "customdata",
"orderNumber": "1"
}];
var offerings = {
"customdata": [{
"code": "Audi",
"color": "black"
}],
"test123": [{
"brand": "Audi",
"color": "black"
}]
}
function sortObjectFromArray(refArray, sortObject, orderKey = 'order', linkKey = 'key') {
// Get copy of refArray
let reference = refArray.slice();
// Sort sortObject [ into an array at this point ]
let sorted = [];
for (let key in sortObject) {
// Searches the refArray for the linkKey, and returns the intended index
let index = reference.find((item) => item[linkKey] === key)[orderKey];
// Places the sortObject's value in the correct index of the 'sorted' Array
sorted[parseInt(index)] = [key, sortObject[key]];
};
// Return an object, created from previous 'sorted' Array
return sorted.reduce((obj, [key, value]) => {
obj[key] = value;
return obj;
}, {});
};
offerings = sortObjectFromArray(subgroup, offerings, 'orderNumber', 'name');
console.log(offerings);

Unable to convert a nested object into an array of objects and viceversa

I am having an object of the following kind:
var sourceObj = {
"feature1": [
{"id":"1","name":"abc","enabled":false,"type":"type1"},
{"id":"2","name":"xyz","enabled":false,"type":"type1"}
]
,
"feature2": [
{"id":"3","name":"lmn","enabled":true,"type":"type2"},
{"id":"4","name":"pqr","enabled":false,"type":"type2"}
]
}
Need to get converted to an array of objects of the following type:
var destArr = [
{ "feature_name":"feature1",
"feature_details":[
{"id":"1","name":"abc","enabled":false,"type":"type1"},
{"id":"2","name":"xyz","enabled":true,"type":"type1"}
]
},
{ "feature_name":"feature2",
"feature_details":[
{"id":"3","name":"lmn","enabled":true,"type":"type2"}
{"id":"4","name":"pqr","enabled":false,"type":"type2"}
]
}
]
I have tried the following approaches for conversion of source object to resultant array of objects and resultant array of objects back to the source object
//Converting source object to array of objects
let arr = Object.keys(sourceObj).map(key => {
return sourceObj[key];
})
converting array of objects back to source objetc
let obj = Object.assign({}, ...destArr.map(item => ({ [item.name]: item.value })));
You could use Object.entries to map everything in a single shot.
To go back to the original structure, you could use reduce against the generated array (see backToOriginalArray below)
var sourceObj = {
"feature1": [{
"id": "1",
"name": "abc",
"enabled": false,
"type": "type1"
},
{
"id": "2",
"name": "xyz",
"enabled": false,
"type": "type1"
}
],
"feature2": [{
"id": "3",
"name": "lmn",
"enabled": true,
"type": "type2"
},
{
"id": "4",
"name": "pqr",
"enabled": false,
"type": "type2"
}
]
};
// Step 1: use object.entries against the original object to build an array of objects.
var destArray = Object.entries(sourceObj).map(([key, value]) => ({
"feature_name": key,
"feature_details": value
}));
console.log(destArray);
// Step 2: use reduce against the generated array to get an object with the same structure of the original one.
var backToOriginalArray = destArray.reduce((acc, {feature_name, feature_details}) => (acc[feature_name] = feature_details, acc), {});
console.log(backToOriginalArray);
Map the Object.entries of your initial object:
var sourceObj = {
"feature1": [
{"id":"1","name":"abc","enabled":false,"type":"type1"},
{"id":"2","name":"xyz","enabled":false,"type":"type1"}
],
"feature2": [
{"id":"3","name":"lmn","enabled":true,"type":"type2"},
{"id":"4","name":"pqr","enabled":false,"type":"type2"}
]
};
const destArr = Object.entries(sourceObj).map(
([feature_name, feature_details]) => ({ feature_name, feature_details })
);
console.log(destArr);
To go the other way around, use Object.fromEntries:
const arr=[{feature_name:"feature1",feature_details:[{id:"1",name:"abc",enabled:!1,type:"type1"},{id:"2",name:"xyz",enabled:!1,type:"type1"}]},{feature_name:"feature2",feature_details:[{id:"3",name:"lmn",enabled:!0,type:"type2"},{id:"4",name:"pqr",enabled:!1,type:"type2"}]}];
const obj = Object.fromEntries(arr.map(
({ feature_name, feature_details }) => [feature_name, feature_details]
));
console.log(obj);

Check if key-value pair exists anywhere in multi-dimensional object

Let's say I want to identify if a key/value-pair exists in any type/level of object, without knowing the object structure before hand. Is this possible?
Example:
Pair to search for: "kod": "REVH"
Object:
{
"names": [{
"name1": "xxx",
"name2": "yyy",
"pn": null,
"functions": [{
"kod": "LE",
"text": "test"
}, {
"kod": "VD",
"text": "test"
}]
}, {
"name1": null,
"name2": "Comp",
"pn": null,
"pn2": "1239992",
"functions": [{
"kod": "REV",
"text": "temp"
}]
}, {
"name1": "Peter",
"name2": "name",
"pn": "2192992",
"pn2": null,
"functions": [{
"kod": "REVH",
"text": "temp"
}]
}]
}
This is possible by using JSON.stringify() and String.prototype.indexOf(). You can simply expose a function that return whether or not the String search is in the Object
let obj={names:[{name1:"xxx",name2:"yyy",pn:null,functions:[{kod:"LE",text:"test"},{kod:"VD",text:"test"}]},{name1:null,name2:"Comp",pn:null,pn2:"1239992",functions:[{kod:"REV",text:"temp"}]},{name1:"Peter",name2:"name",pn:"2192992",pn2:null,functions:[{kod:"REVH",text:"temp"}]}]};
function pairInObject (obj, search) {return JSON.stringify(obj).indexOf(search) != -1 }
console.log(pairInObject(obj,'"kod":"REVH"'))
console.log(pairInObject(obj,'"foo":"bar"'))
This recursive solution uses Array.some(), and Object.values() to iterate the contents of an object/array, and look for the key/value combination:
const searchKeyValue = (data, key, value) => {
// if falsy or not an object/array return false
if(!data || typeof data !== 'object') return false;
// if the value of the key equals value return true
if(data[key] === value) return true;
// return the results of using searchKeyValue on all values of the object/array
return Object.values(data).some((data) => searchKeyValue(data, key, value));
};
const data = {"names":[{"name1":"xxx","name2":"yyy","pn":null,"functions":[{"kod":"LE","text":"test"},{"kod":"VD","text":"test"}]},{"name1":null,"name2":"Comp","pn":null,"pn2":"1239992","functions":[{"kod":"REV","text":"temp"}]},{"name1":"Peter","name2":"name","pn":"2192992","pn2":null,"functions":[{"kod":"REVH","text":"temp"}]}]};
console.log(searchKeyValue(data, 'kod', 'REVH')); // true
console.log(searchKeyValue(data, 'text', null)); // false
console.log(searchKeyValue(data, 'pn', null)); // true

Iterate through the child objects and get all the values with javascript

var formmd = {
"frmType": "Registration",
"frmStage": "step1-complete",
"formattr": {
"SystemUser": {
"LoginName": "A#test.com",
"Password": "password",
"PIN": "",
"IsTestUser": false
},
"ConsumerAddress": {
"AddressLine1": "201 MOUNT Road",
"AddressLine2": null,
"AddressTypeId": "1",
"City": "OLA TRAP",
"State": "NM",
"Zipcode": "60005"
},
"ConsumerPhone": {
"PhoneTypeId": 6,
"PhoneNumber": "9876543210",
"PrimaryPhoneIndicator": null,
"AllowVoicemail": false
},
"PhysicianSpecialty": {
"SpecialtyList": [
"1",
"2"
]
},
}
}
I'm trying to fetch all the values of the child objects under formattr but I'm unable to iterate inside the child objects. The following is the script I tried.
My Result should be
"A#test.com"
"password"
"PIN": ""
False
201 MOUNT Road"
The script I tried is
function walk(formmd) {
var obj1 = formmd.formattr;
for(var key in obj1){
if (obj1.hasOwnProperty(key)) {
var val1 = obj1[key];
if(val1.hasOwnProperty(key)){
for(var key in val1){
var val2 =val1[key];
console.log("val2");
}
}
}
}
}
How to access the keys of child objects in an automated way?
Try like this
for (var key in formmd) {
var val1 = formmd[key];
if (key=="formattr") {
for (var key1 in val1) {
var val2 = val1[key1];
for(var key2 in val2)
console.log(val2[key2]);
}
}
}
DEMO
You might find it easier to understand code written in a functional style. This is one solution, which I'll explain:
Object.values(formmd.formattr)
.map(obj => Object.values(obj))
.reduce((acc, vals) => acc.concat(vals), [])
The first expression Object.values(formmd.formattr) gives you an array of all the values (not keys) under formmd.formattr. Something like:
[{"LoginName": "A#test.com", "Password": "password", …}, {"AddressLine1": "201 MOUNT Road", "AddressLine2": null, …}, …]
Since you want the values within each of these sub-objects the next line .map(obj => Object.values(obj)) will do just that. It returns a new array where each object in the input array is transformed through Object.values(…). It returns something like:
[["A#test.com", "password", "", false], ["201 MOUNT Road", null, "1", …], …]
This array has all the data you're after, but in nested arrays, so it needs to be flattened with .reduce((acc, vals) => acc.concat(vals), []). This reduce will successively concat these sub-arrays to produce a single array like:
["A#test.com", "password", "", false, "201 MOUNT Road", null, "1", …]
which contains all the values of the child objects under formattr.
Here's some other ways to do it:
Object.values(formmd.formattr)
.reduce((acc, x) => acc.concat(Object.values(x)), [])
or
[].concat(...
Object.values(formmd.formattr)
.map(obj => Object.values(obj)))
You will have to use Object.entries()
The Object.entries() method returns an array of a given object's own
enumerable string-keyed property [key, value] pairs, in the same order
as that provided by a for...in loop. (The only important difference is
that a for...in loop enumerates properties in the prototype chain as
well).
Example -
for (const [key, value] of Object.entries(objectName)) {
console.log(`${key}: ${value}`);
}
Code Snippet -
var formmd = {
"frmType": "Registration",
"frmStage": "step1-complete",
"formattr": {
"SystemUser": {
"LoginName": "A#test.com",
"Password": "password",
"PIN": "",
"IsTestUser": false
},
"ConsumerAddress": {
"AddressLine1": "201 MOUNT Road",
"AddressLine2": null,
"AddressTypeId": "1",
"City": "OLA TRAP",
"State": "NM",
"Zipcode": "60005"
},
"ConsumerPhone": {
"PhoneTypeId": 6,
"PhoneNumber": "9876543210",
"PrimaryPhoneIndicator": null,
"AllowVoicemail": false
},
"PhysicianSpecialty": {
"SpecialtyList": [
"1",
"2"
]
},
}
}
for (const [key, value] of Object.entries(formmd.formattr)) {
console.log('Value',value);
}

Categories

Resources