Searching the entire JSON tree based on keys at every level - javascript

I am trying to Search in a tree, but my results are not as expected. Can any body please help?
function treeSearch(searchedValue,ExceptionTree:){
let result = {};
var childrenKeys = Object.keys(ExceptionTree);
for(let i = 0; i< childrenKeys.length;i++){
if(childrenKeys[i].toLowerCase().indexOf(searchedValue) >=0 || Object.keys( treeSearch( ExceptionTree[childrenKeys[i]] , searchedValue ) ).length >=0 )
result[childrenKeys[i]] = ExceptionTree[childrenKeys[i]];
}
return result;
}
Below is the sample Input:
var myTree= {
"North America": {
"Canada": {
"VanCouver": 1,
"Ottawa": 2
},
"US": {
"Florida": 3,
"Texas": 4
}
},
"Asia": {
"India": {
"Mumbai": 5,
"Delhi": 6
},
"China": {
"Shanghai": 9,
"Beijing": 10
}
}
}
If I call
treeSearch("Texas",myTree)
the result should be
{
"North America": {
"USA": {
"Texas":4
}
}
}
I am either getting the entire tree returned or an empty tree. Any suggestions?

Try this (details in comments):
// Insert your tree and text to find
function treeSearch(tree, text) {
let result = null;
// Loop input tree
for (const [key, value] of Object.entries(tree)) {
if (typeof(value) === "object") {
// Recursive call on an sub-objects
const found = treeSearch(value, text);
if (found) {
result = { [key]: found };
}
} else if (key === text) {
// Result found
result = { [key]: value };
}
}
return result;
}
const result = treeSearch(myTree, "Texas");
Result is an object below or null if text wasn't found
{
North America: {
US: {
Texas: 4
}
}
}

Here is an iterative solution using object-scan
// const objectScan = require('object-scan');
const myTree = { 'North America': { Canada: { VanCouver: 1, Ottawa: 2 }, US: { Florida: 3, Texas: 4 } }, Asia: { India: { Mumbai: 5, Delhi: 6 }, China: { Shanghai: 9, Beijing: 10 } } };
const treeSearch = (name, tree) => objectScan(['*.*.*'], {
abort: true,
filterFn: ({ property, key, value, context }) => {
if (property !== name) {
return false;
}
key.reduce((p, c, idx) => {
p[c] = idx === key.length - 1 ? value : {};
return p[c];
}, context);
return true;
}
})(tree, {});
console.log(treeSearch('Texas', myTree));
// => { 'North America': { US: { Texas: 4 } } }
.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

Related

Convert object to array of prorperties

I need to convert object:
{
middleName: null,
name: "Test Name",
university: {
country: {
code: "PL"
},
isGraduated: true,
speciality: "Computer Science"
}
}
to array:
[{
key: "name",
propertyValue: "Test Name",
},
{
key: "middleName",
propertyValue: null,
},
{
key: "university.isGraduated",
propertyValue: true,
},
{
key: "university.speciality",
propertyValue: "Computer Science",
},
{
key: "university.country.code",
propertyValue: "PL"
}];
I wrote algorithm, but it's pretty dummy, how can I improve it? Important, if object has nested object than I need to write nested object via dot (e.g university.contry: "value")
let arr = [];
Object.keys(parsedObj).map((key) => {
if (parsedObj[key] instanceof Object) {
Object.keys(parsedObj[key]).map((keyNested) => {
if (parsedObj[key][keyNested] instanceof Object) {
Object.keys(parsedObj[key][keyNested]).map((keyNestedNested) => {
arr.push({ 'key': key + '.' + keyNested + '.' + keyNestedNested, 'propertyValue': parsedObj[key][keyNested][keyNestedNested] })
})
} else {
arr.push({ 'key': key + '.' + keyNested, 'propertyValue': parsedObj[key][keyNested] })
}
})
} else {
arr.push({ 'key': key, 'propertyValue': parsedObj[key] })
}
});
Thanks
A recursive function implementation.
I have considered that your object consist of only string and object as the values. If you have more kind of data types as your values, you may have to develop on top of this function.
const myObj = {
middleName: null,
name: "Test Name",
university: {
country: {
code: "PL"
},
isGraduated: true,
speciality: "Computer Science"
}
}
const myArr = [];
function convertObjectToArray(obj, keyPrepender) {
Object.entries(obj).forEach(([key, propertyValue]) => {
if (typeof propertyValue === "object" && propertyValue) {
const updatedKey = keyPrepender ? `${keyPrepender}.${key}` : key;
convertObjectToArray(propertyValue, updatedKey)
} else {
myArr.push({
key: keyPrepender ? `${keyPrepender}.${key}` : key,
propertyValue
})
}
})
}
convertObjectToArray(myObj);
console.log(myArr);
I'd probably take a recursive approach, and I'd probably avoid unnecessary intermediary arrays (though unless the original object is massive, it doesn't matter). For instance (see comments):
function convert(obj, target = [], prefix = "") {
// Loop through the object keys
for (const key in obj) {
// Only handle "own" properties
if (Object.hasOwn(obj, key)) {
const value = obj[key];
// Get the full key for this property, including prefix
const fullKey = prefix ? prefix + "." + key : key;
if (value && typeof value === "object") {
// It's an object...
if (Array.isArray(value)) {
throw new Error(`Arrays are not valid`);
} else {
// ...recurse, providing the key as the prefix
convert(value, target, fullKey);
}
} else {
// Not an object, push it to the array
target.push({key: fullKey, propertyValue: value});
}
}
}
// Return the result
return target;
}
Live Example:
const original = {
middleName: null,
name: "Test Name",
university: {
country: {
code: "PL"
},
isGraduated: true,
speciality: "Computer Science"
}
};
function convert(obj, target = [], prefix = "") {
// Loop through the object keys
for (const key in obj) {
// Only handle "own" properties
if (Object.hasOwn(obj, key)) {
const value = obj[key];
// Get the full key for this property, including prefix
const fullKey = prefix ? prefix + "." + key : key;
if (value && typeof value === "object") {
// It's an object...
if (Array.isArray(value)) {
throw new Error(`Arrays are not valid`);
} else {
// ...recurse, providing the key as the prefix
convert(value, target, fullKey);
}
} else {
// Not an object, push it to the array
target.push({key: fullKey, propertyValue: value});
}
}
}
// Return the result
return target;
}
const result = convert(original, []);
console.log(result);
.as-console-wrapper {
max-height: 100% !important;
}
Note that I've assumed the order of the array entries isn't significant. The output you said you wanted is at odds with the standard order of JavaScript object properties (yes, they have an order now; no, it's not something I suggest relying on 😀). I've gone ahead and not worried about the order within an object.
This is the most bulletproof I could do :D, without needing a global variable, you just take it, and us it wherever you want!
const test = {
middleName: null,
name: "Test Name",
university: {
country: {
code: "PL"
},
isGraduated: true,
speciality: "Computer Science"
}
};
function toPropertiesByPath(inputObj) {
let arr = [];
let initialObj = {};
const getKeys = (obj, parentK='') => {
initialObj = arr.length === 0 ? obj: initialObj;
const entries = Object.entries(obj);
for(let i=0; i<entries.length; i++) {
const key = entries[i][0];
const val = entries[i][1];
const isRootElement = initialObj.hasOwnProperty(key);
parentK = isRootElement ? key: parentK+'.'+key;
if(typeof val === 'object' && val!==null && !Array.isArray(val)){
getKeys(val, parentK);
} else {
arr.push({ key: parentK, property: val });
}
}
};
getKeys(inputObj);
return arr;
}
console.log(toPropertiesByPath(test));
I wrote a small version using recursive function and another for validation is an object.
let values = {
middleName: null,
name: "Test Name",
university: {
country: {
code: "PL"
},
isGraduated: true,
speciality: "Computer Science"
}
}
function isObject(obj) {
return obj != null && obj.constructor.name === "Object"
}
function getValues(values) {
let arrValues = Object.keys(values).map(
v => {
return { key: v, value: isObject(values[v]) ? getValues(values[v]) : values[v] };
});
console.log(arrValues);
}
getValues(values);

dynamically generated deep objects

I want to ask for help with the problem. I have an existing deep Javascript object from which I want to dynamically generate multiple versions.
I have a method that has 2 parameters.
first: the object from which I want to generate new ones,
second: a number or an array of numbers
for example:
let myObj = {
brown: {
50: '#f9f8f2',
100: '#f3f0e6',
},
singleProp: '#e6b01e',
propLvl1: {
color: '#32a852',
sub1: {
color: '#44eff2',
sub2: {
color: '#f2448d'
},
},
},
};
myFunction(myObject, [10, 30]);
my goal would be:
MY-10-brown: {
50: '#(DYNAMICVALUE)f9f8f2',
100: '#(DYNAMICVALUE)f3f0e6',
},
MY-10-singleProp: '#(DYNAMICVALUE)e6b01e',
MY-10-propLvl1: {
color: '#(DYNAMICVALUE)32a852',
sub1: {
color: '#(DYNAMICVALUE)44eff2',
sub2: {
color: '#(DYNAMICVALUE)f2448d'
},
},
}
MY-30-brown: {
50: '#(DYNAMICVALUE)f9f8f2',
100: '#(DYNAMICVALUE)f3f0e6',
},
MY-30-singleProp: '#(DYNAMICVALUE)e6b01e',
MY-30-propLvl1: {
color: '#(DYNAMICVALUE)32a852',
sub1: {
color: '#(DYNAMICVALUE)44eff2',
sub2: {
color: '#(DYNAMICVALUE)f2448d'
},
},
}
So far I have reached him:
export default function generateObjects(obj, numbers) {
let newObj = {};
for (let q = 0; q < transparentValue.length; q += 1) {
let Obj = doTheJob(obj, transparentValue[q]);
Object.assign(newObj, Obj);
}
return newObj;
}
function doTheJob(obj, number) {
const newObj = {};
let newKey = '';
Object.keys(obj).forEach(function (key) {
let trim = `${obj[key]}`.substring(1);
let newValue = `#${anotherObject[number]}${trim}`;
if (typeof obj[key] === 'object') {
newKey = `MY-${number}-${key}`;
newObj[newKey] = obj[key];
generateNewObj(newObj[newKey], number);
return;
}
if (typeof obj[key] === 'string') {
newObj[key] = newValue;
}
});
return newObj;
}
You could create new properties for the first level of the object and take copies of data.
const
copy = value => typeof value === 'object'
? Object.fromEntries(Object.entries(value).map(([k, v]) => [k, copy(v)]))
: typeof value === 'string'
? value.replace('#', '#DYNAMICVALUE')
: value
create = (object, values, header) => Object.fromEntries(Object
.entries(object)
.reduce((r, [k, v]) => [...r, ...values.map(i => [[header, i, k].join('-'), copy(v)])], [])
),
myObj = { brown: { 50: '#f9f8f2', 100: '#f3f0e6' }, singleProp: '#e6b01e', propLvl1: { color: '#32a852', sub1: { color: '#44eff2', sub2: { color: '#f2448d' } } } };
console.log(create(myObj, [10, 30], 'my'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can:
Create a new object
Loop through each number in the array
Inside the loop, loop through each property in the object and assign the value of the property to the new object's modified property ("MY-"+num+"-"+prop).
let myObj = {
brown: {
50: '#f9f8f2',
100: '#f3f0e6',
},
singleProp: '#e6b01e',
propLvl1: {
color: '#32a852',
sub1: {
color: '#44eff2',
sub2: {
color: '#f2448d'
},
},
},
};
function process(obj, numArr){
const newObj = {};
for(const num of numArr){
for(const prop in obj){
newObj['MY-'+num+'-'+prop] = obj[prop];
}
}
return newObj;
}
console.log(JSON.stringify(process(myObj, [10, 30]), 0, 2))
.as-console-wrapper{max-height:100%!important;top:0}

Get path to JSON object by key?

Given the following object:
const ourObject = {
"payload": {
"streams": [
{
"children": {
"2165d20a-6276-468f-a02f-1abd65cad618": {
"additionalInformation": {
"narrative": {
"apple": "A",
"banana": "B"
},
"myInventory": {
"fruits": [
{
"name": "apple"
},
{
"name": "banana"
}
]
}
}
}
}
}
]
}
};
We're trying to find the path of myInventory, the issue is that the children's uuid will be different each time. Any idea how we can get the path to myInventory by providing it as a key and get the json path to it?
If things are dynamic, a programmatic key search could help
const ourObject = {
"payload": {
"streams": [
{
"children": {
"2165d20a-6276-468f-a02f-1abd65cad618": {
"additionalInformation": {
"narrative": {
"apple": "A",
"banana": "B"
},
"myInventory": {
"fruits": [
{
"name": "apple"
},
{
"name": "banana"
}
]
}
}
}
}
}
]
}
};
const getPath = (key, o) => {
if (!o || typeof o !== "object") {
return "";
}
const keys = Object.keys(o);
for(let i = 0; i < keys.length; i++) {
if (keys[i] === key ) {
return key;
}
const path = getPath(key, o[keys[i]]);
if (path) {
return keys[i] + "." + path;
}
}
return "";
};
const getValueForKey = (key, o) => {
if (!o || typeof o !== "object") {
return undefined;
}
const keys = Object.keys(o);
for(let i = 0; i < keys.length; i++) {
if (keys[i] === key ) {
return o[key];
}
const value = getValueForKey(key, o[keys[i]]);
if (value) {
return value;
}
}
return undefined;
}
console.log(getPath("myInventory", ourObject))
console.log(getValueForKey("myInventory", ourObject))
Not sure if I understand the question right but
let uuid = '2165d20a-6276-468f-a02f-1abd65cad618';
ourObject.payload.streams[0].children[uuid].additionalInformation.myInventory
var changingKey = Object.keys(ourObject["payload"]["streams"][0]["children"])[0];
console.log(ourObject["payload"]["streams"][0]["children"][changingKey]["additionalInformation"]["myInventory"]);
Okay, you could create a helper function that gets the UUID. Since it's an object, the lookup is close to O(1) especially given the case that the children has only one key-value pair here.
function getUUIDFromPayload(payload) {
let obj = payload.streams[0].children
let uuid = Object.keys(obj)[0]
return uuid
}
Usage
const uuid = getUUIDFromPayload(payload)
ourObject.payload.streams[0].children[uuid].additionalInformation.myInventory

How to merge two objects of all properties in an array if it has same id in javascript?

JSON: (I have two same object properties in array. i would like merge all the property values of this array. I have added other case, if id gets changed, it should give like below. )
const array = [{
Lot_id:{
id:1,
qty_receive:30,
qty_return:5,
qty_remain:15
},
qty_allocate:10,
remaining_qty:20
},
{
Lot_id:{
id:1,
qty_receive:30,
qty_return:5,
qty_remain:15
},
qty_allocate:10,
remaining_qty:20
},
{
Lot_id:{
id:2,
qty_receive:30,
qty_return:5,
qty_remain:15
},
qty_allocate:10,
remaining_qty:20
}]
expected result:(updated my question)
const array = [{
Lot_id:{
id:1,
qty_receive:60,
qty_return:10,
qty_remain:30
}
qty_allocate:20,
remaining_qty:40
},
{
Lot_id:{
id:2,
qty_receive:30,
qty_return:5,
qty_remain:15
},
qty_allocate:10,
remaining_qty:20
}]
Can u try this?
const array = [
{ Lot_id:{ id:1, qty_receive:30, qty_return:5, qty_remain:15}, qty_allocate:10},
{ Lot_id:{ id:1, qty_receive:30, qty_return:5, qty_remain:15}, qty_allocate:10},
{ Lot_id:{ id:2, qty_receive:30, qty_return:5, qty_remain:15}, qty_allocate:10},
]
function merge(array){
var result = [];
array.reduce(function(res, value) {
if (!res[value.Lot_id.id]) {
res[value.Lot_id.id] = {
Lot_id: {
id:value.Lot_id.id,
qty_receive:0,
qty_return:0,
qty_remain:0,
}, qty_allocate: 0
};
result.push(res[value.Lot_id.id])
}
res[value.Lot_id.id].qty_allocate += value.qty_allocate;
res[value.Lot_id.id].Lot_id.qty_receive += value.Lot_id.qty_receive;
res[value.Lot_id.id].Lot_id.qty_return += value.Lot_id.qty_return;
res[value.Lot_id.id].Lot_id.qty_remain += value.Lot_id.qty_remain;
return res;
}, {});
return result
}
console.log(merge(array));
const reduceArr = array.reduce((total, {Lot_id: {qty_receive, qty_return, qty_remain}, qty_allocate}) => {
total.Lot_id.qty_receive += qty_receive;
total.Lot_id.qty_return += qty_return;
total.Lot_id.qty_remain += qty_remain;
total.qty_allocate += qty_allocate;
return [total];
});
Update answer according to your updated question:
const mergeArr = () => {
const newArr = [];
let tempId = 0;
array.forEach((item, index) => {
if (tempId !== item.Lot_id.id) {
tempId = item.Lot_id.id;
newArr.push(item);
} else {
const lastItem = newArr[newArr.length - 1];
const lastLot = lastItem.Lot_id;
const newLot = item.Lot_id;
lastLot.qty_receive += newLot.qty_receive;
lastLot.qty_return += newLot.qty_return;
lastLot.qty_remain += newLot.qty_remain;
lastItem.remaining_qty += item.remaining_qty;
lastItem.qty_allocate += item.qty_allocate;
}
});
return newArr;
}
console.log(mergeArr());
try the code here:
https://repl.it/repls/SparseUnfortunateLanguage
Try this instead,
let result = mergeElements(array);
console.log(result);
/**
* function accepts the array of objects you need to merge by adding fields
* */
function mergeElements(array = []) {
let sumOf = [];
if (array.length <= 1) {
return array;
} else {
sumOf[0] = array[0];
let index = 0;
array.forEach(function (item, index) {
if (index != 0) {
sumOf[0] = iterateItem(item, sumOf[0]);
}
index++;
});
}
return sumOf;
}
/**
*function for indepth iteration of objects
* */
function iterateItem(item, sumOf) {
if (typeof item == "object") {
for (let key in item) {
if (typeof item[key] != "object") {
if (typeof sumOf[key] == "undefined") {
sumOf[key] = 0;
}
if (key == "id") {
continue;
}
sumOf[key] += item[key];
} else {
if (typeof sumOf[key] == "undefined") {
sumOf[key] = {};
}
sumOf[key] = iterateItem(item[key], sumOf[key]);
}
}
} else {
sumOf += item;
}
return sumOf;
}
I hope this will help you to solve your problem.
You can check my solution for your issue.
const array = [
{
Lot_id: {
id: 1,
qty_receive: 30,
qty_return: 5,
qty_remain: 15,
},
qty_allocate: 10,
},
{
Lot_id: {
id: 1,
qty_receive: 30,
qty_return: 5,
qty_remain: 15,
},
qty_allocate: 10,
},
{
another_id: {
id: 1,
qty_receive: 30,
qty_return: 5,
qty_remain: 15,
},
qty_allocate: 10,
},
{
another_id: {
id: 1,
qty_receive: 30,
qty_return: 5,
qty_remain: 15,
},
qty_allocate: 10,
},
];
const isObject = (value) => {
return (
typeof value === "object" &&
value instanceof Object &&
!(value instanceof Array)
);
};
const result = array.reduce((acc, obj) => {
const existingObj = acc.find((accObj) => {
return Object.keys(accObj).every(
(key) => Object.keys(obj).indexOf(key) > -1
);
});
if (existingObj) {
Object.keys(obj).forEach((key) => {
if (isObject(obj[key])) {
const innerObj = obj[key];
const existingInnerObj = existingObj[key];
Object.keys(innerObj).forEach((innerKey) => {
if(innerKey !== 'id') {
existingInnerObj[innerKey] += innerObj[innerKey];
}
});
} else {
existingObj[key] += obj[key];
}
});
} else {
acc.push(obj);
}
return acc;
}, []);
console.log(result);

Convert an Object into an Object of Objects

I have an Object:
{
"Results": {
"circle": 0.06879016757011414,
"quad": {
"exp": 0.8039023876190186,
"actual": 0.19609761238098145
},
"square": 0.8266428709030151
}
}
I want to convert it to:
{
"Results": {
"circle": {
"circle": 0.06879016757011414
},
"quad": {
"exp": 0.8039023876190186,
"actual": 0.19609761238098145
},
"square": {
"square": 0.8266428709030151
}
}
}
Have tried this code:
var modData = {};
data = data.Results;
for (var key in data) {
if (data.hasOwnProperty(key)) {
modData[key] = data[key];
for (var innerKey in data[key]) {
modData[key] = data[key];
}
}
}
console.log("Modified is:", modData);
Unfortunately, this still returns the original object, what is it that I am doing which is wrong?
A jquery solution is fine as well.
Loop trough the properties with for .. in and any property is not an object replace it with one. Like this:
let x = {
"Results": {
"circle": 0.06879016757011414,
"quad": {
"exp": 0.8039023876190186,
"actual": 0.19609761238098145
},
"square": 0.8266428709030151
}
}
for (key in x.Results) {
if (typeof x.Results[key] !== 'object')
x.Results[key] = {
[key]: x.Results[key]
}
}
console.log(x);
If you want to preserve the original object do this:
let data = {
"Results": {
"circle": 0.06879016757011414,
"quad": {
"exp": 0.8039023876190186,
"actual": 0.19609761238098145
},
"square": 0.8266428709030151
}
}
data = data.Results;
modData = {};
for (key in data) {
if (typeof data[key] !== 'object')
modData[key] = { [key]: data[key] }
else
modData[key] = { [key]: data[key] }
}
console.log(modData);
You could check the type and if not an object then assign a new object with the same key and value.
This proposal uses computed property names for getting a variable as key for an object.
var object = { Results: { circle: 0.06879016757011414, quad: { exp: 0.8039023876190186, actual: 0.19609761238098145 }, square: 0.8266428709030151 } };
Object
.keys(object.Results)
.forEach(function (k) {
if (object.Results[k] && typeof object.Results[k] !== 'object') {
object.Results[k] = { [k]: object.Results[k] };
}
});
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The value that should be stored in modData should be the object itself rather than the value. This code gives the expected value
var modData = {};
data = data.Results;
for (var key in data) {
if (data.hasOwnProperty(key)) {
var temp = Object();
temp[key] = data[key];
modData[key] = temp;
for (var innerKey in data[key]) {
var temp = Object();
temp[key] = data[key];
modData[key] = temp;
}
}
}
const input = {
"Results": {
"circle": 0.06879016757011414,
"quad": {
"exp": 0.8039023876190186,
"actual": 0.19609761238098145
},
"square": 0.8266428709030151
}
};
const result = {
Results: {}
};
//If you want object construct specifically for "circle" & "square"
for (let key in input.Results) {
if (input.Results.hasOwnProperty(key)) {
if (key === 'circle') {
result.Results.circle = {
'circle': input.Results[key]
}
} else if (key === 'square') {
result.Results.square = {
'square': input.Results[key]
}
} else {
result.Results[key] = input.Results[key];
}
}
}
//Generically construct object if key is not object
for (let key in input.Results) {
if (input.Results.hasOwnProperty(key)) {
if (key !== 'object') {
result.Results[key] = {
[key]: input.Results[key]
}
}
}
}
console.log(result);

Categories