I've figured out how to create an object based on an array, now I'm trying to understand how to build an array back from that object.
with the object
{
social: {
children: {
swipes: {
children: {
women: null,
men: null
}
}
}
},
upgrade: {
children: {
premium: null
}
}
}
how do I create an array of
['/social/swipes/women', '/social/swipes/men', '/upgrade/premium']
?
so far I've just written a function to iterate through the object
let iterate = obj => {
const urls = [];
for (let k in obj) {
if (obj[k] !== null && obj[k].hasOwnProperty('children')) {
console.log('iterating through key: ', k)
iterate(obj[k].children)
} else {
console.log(k, 'is null')
}
}
}
I'd use a generator for that:
function* paths(obj, previous = "") {
for(const [key, value] of Object.entries(obj)) {
if(typeof value === "object" && value !== null) {
yield* paths(value.children, previous + "/" + key);
} else {
yield previous + "/" + key;
}
}
}
That can be called as:
console.log([...paths({ social: { /*...*/ } })]);
Here's a simple recursive approach that avoids adding any children keys to the path:
const pathify = (data, path = "", res = []) => {
Object.keys(data).forEach(k => {
if (data[k] === null) {
res.push(`${path}/${k}`);
}
else {
pathify(data[k], path + (k === "children" ? "" : `/${k}`), res);
}
});
return res;
};
console.log(
pathify({
social: {
children: {
swipes: {
children: {
women: null,
men: null
}
}
}
},
upgrade: {
children: {
premium: null
}
}
})
);
You could take an iterative and recursive approach by collecting all keys and then build the joined strings.
function getKeys(object) {
return Object
.entries(object)
.reduce((r, [k, v]) =>
r.concat(v && typeof v === 'object' && v.children
? getKeys(v.children).map(sub => [k].concat(sub))
: k
),
[]
);
}
var data = { social: { children: { swipes: { children: { women: null, men: null } } } }, upgrade: { children: { premium: null } } },
result = getKeys(data).map(a => a.join('/'));
console.log(result);
Same with a generator and a signature without a second parameter for the collecting array.
function* getKeys(object) {
var k;
for ([k, v] of Object.entries(object)) {
if (v && typeof v === 'object' && v.children) {
yield* Array.from(getKeys(v.children), sub => [k].concat(sub));
} else {
yield [k];
}
}
}
var data = { social: { children: { swipes: { children: { women: null, men: null } } } }, upgrade: { children: { premium: null } } },
result = Array.from(getKeys(data), a => a.join('/'));
console.log(result);
Related
Hi everyone I have following code
I want to show my DefaultValue nested objects like this
["DefaultValue", "DefaultValue", "DefaultValue","DefaultValue","DefaultValue","DefaultValue"]
I have following data from backend:
const data = [
{
id: 243,
Name: "test",
type: "checkbox",
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: ["a"]
}
}
}
}
}
}
];
So I am trying to do following, but it's not works, its says like Cannot convert undefined or null to object
const innerObject = o => {
return Object.keys(o).reduce(function (r, k) {
return typeof o[k] === 'object' ? innerObject(o[k]) : ((r[k] = o[k]), r);
}, {});
};
Please help me to resolve this problem.
you can try this:
const data = [
{
id: 243,
Name: "test",
type: "checkbox",
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: ["a"]
}
}
}
}
}
}
];
const makeArr = (obj, arr = []) =>{
if(typeof obj === 'object' && obj !== null){
arr.push('DefaultValue');
return makeArr(obj.DefaultValue, arr)
}else{
return arr;
}
}
console.log(makeArr(data[0].DefaultValue))
I got this type of object:
const obj = {
group: {
data: {
data: [
{
id: null,
value: 'someValue',
data: 'someData'
}
]
}
}
};
I need to edit this object so whenever null is in the property value,
it would be replaced with some string.
Meaning if the replacement string will be 'someId',
the expected outcome is:
const obj = {
group: {
data: {
data: [
{
id: 'someId',
value: 'someValue',
data: 'someData'
}
]
}
}
};
Closest I found were this and this but didn't manage to manipulate the solutions there to what i need.
How should I do it?
Probably running into issues with the array values. Pass in the index of the array to modify. In this case [0]
obj.group.data.data[0].id = "someId"
EDIT
This will update all null values of id inside the data array:
obj.group.data.data.forEach(o => {
if (o.id === null) {
o.id = "someId"
}
})
Another EDIT
Here is an algorithm to recursively check all deeply nested values in an object. It will compile an array of object paths where null values live. There is an included helper method to find and update the value of the object at the given path in the array. There is a demonstration of the program in the console.
const object = {
group: {
data: {
data: [
{
id: null,
value: "foo",
data: [null, "bar", [null, { stuff: null }]]
},
{
id: null,
value: null,
data: {
bar: [null]
}
},
{
id: null,
value: "foo",
data: null
},
{
id: 4,
value: "foo",
data: "bar"
},
{
id: 4,
value: "stuff",
data: null
}
]
},
attributes: null,
errors: ["stuff", null]
}
}
const inspectProperty = (key, obj, path = "") => {
if (typeof obj[key] === "object") {
if (obj[key] instanceof Array) {
return analyzeArray(obj[key], `${path ? path + "." : ""}${key}`);
}
return analyzeObj(obj[key], `${path ? path + "." : ""}${key}`);
}
return [];
};
const analyzeKey = (obj, key, path = "") => {
if (obj[key] === null) return [`${path ? path + "." : ""}${key}`];
return inspectProperty(key, obj, path).reduce((a, k) => [...a, ...k], []);
};
const analyzeObj = (obj, path = "") => {
return Object.keys(obj).map((item) => analyzeKey(obj, item, path));
};
const analyzeArray = (array, path) => {
return array.map((item, i) => analyzeKey(array, i, path));
};
const updateNullValue = (path, value) => {
let p = path.split(".");
p.reduce((accum, iter, i) => {
if (i === p.length - 1) {
accum[iter] = value;
return object;
}
return accum[iter];
}, object);
};
let nullValues = analyzeObj(object)[0]
console.log(nullValues)
nullValues.forEach((nullVal, i) => {
updateNullValue(nullVal, "hello-" + i)
})
console.log(object)
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);
I am trying to get the change object from two objects using typescript in angular.
For example
this.productPreviousCommand = {
"id": "60f910d7d03dbd2ca3b3dfd5",
"active": true,
"title": "ss",
"description": "<p>ss</p>",
"category": {
"id": "60cec05df64bde4ab9cf7460"
},
"subCategory": {
"id": "60cec18c56d3d958c4791117"
},
"vendor": {
"id": "60ced45b56d3d958c479111c"
},
"type": "load_product_success"
}
model = {
"active": true,
"title": "ss",
"description": "<p>ss sss</p>",
"category": "60cec05df64bde4ab9cf7460",
"subCategory": "60cec18c56d3d958c4791117",
"vendor": "60ced45b56d3d958c479111c",
"tags": []
}
Now the difference between two objects are description: "<p>hello hello 1</p>". So I want to return {description: "<p>hello hello 1</p>"}
I used lodash https://github.com/lodash/lodash
import { transform, isEqual, isObject, isArray} from 'lodash';
function difference(origObj, newObj) {
function changes(newObj, origObj) {
let arrayIndexCounter = 0
return transform(newObj, function (result, value, key) {
if (!isEqual(value, origObj[key])) {
let resultKey = isArray(origObj) ? arrayIndexCounter++ : key
result[resultKey] = (isObject(value) && isObject(origObj[key])) ? changes(value, origObj[key]) : value
}
})
}
return changes(newObj, origObj)
}
This library is not working for me, it returns the whole object using this code const differenc = difference(this.productPreviousCommand, model);
The output of above code is
{
active: true
description: "<p>hello hello 1</p>"
id: "60f8f29dd03dbd2ca3b3dfd1"
title: "hello"
}
Try this function
differenceInObj(firstObj: any, secondObj: any): any {
let differenceObj: any = {};
for (const key in firstObj) {
if (Object.prototype.hasOwnProperty.call(firstObj, key)) {
if(firstObj[key] !== secondObj[key]) {
differenceObj[key] = firstObj[key];
}
}
}
return differenceObj;
}
You can check loop through each key of the first object and compare it with the second object.
function getPropertyDifferences(obj1, obj2) {
return Object.entries(obj1).reduce((diff, [key, value]) => {
// Check if the property exists in obj2.
if (obj2.hasOwnProperty(key)) {
const val = obj2[key];
// Check if obj1's property's value is different from obj2's.
if (val !== value) {
return {
...diff,
[key]: val,
};
}
}
// Otherwise, just return the previous diff object.
return diff;
}, {});
}
const a = {
active: true,
description: '<p>hello</p>',
id: '60f8f29dd03dbd2ca3b3dfd1',
title: 'hello',
};
const b = {
active: true,
description: '<p>hello hello 1</p>',
id: '60f8f29dd03dbd2ca3b3dfd1',
title: 'hello',
};
const c = {
active: true,
description: '<p>hello hello 2</p>',
id: '60f8f29dd03dbd2ca3b3dfd1',
title: 'world',
};
console.log(getPropertyDifferences(a, b));
console.log(getPropertyDifferences(b, c));
function difference(origObj, newObj) {
const origObjKeyList = Object.keys(origObj),
newObjKeyList = Object.keys(newObj);
// if objects length is not same
if (origObjKeyList?.length !== newObjKeyList?.length) {
return;
}
// if object keys some difference in keys
if (Object.keys(origObj).filter((val) => !Object.keys(newObj).includes(val))?.length) {
return;
}
return Object.entries(origObj).reduce(
(acc, [key, value]) => (newObj[key] !== value ? { ...acc, ...{ [key]: newObj[key] } } : acc),
[]
);
}
const a = {
active: true,
description: '<p>hello</p>',
id: '60f8f29dd03dbd2ca3b3dfd1',
title: 'hello',
};
const b = {
active: true,
description: '<p>hello hello 1</p>',
id: '60f8f29dd03dbd2ca3b3dfd1',
title: 'hello',
};
console.log(difference(a, b));
You can try this code.
function difference(origObj, newObj) {
const origObjKeyList = Object.keys(origObj),
newObjKeyList = Object.keys(newObj);
// if objects length is not same
if (origObjKeyList?.length !== newObjKeyList?.length) {
return;
}
// if object keys is not same
if (Object.keys(origObj).filter((val) => !Object.keys(newObj).includes(val))?.length) {
return;
}
return Object.entries(origObj).reduce(
(acc, [key, value]) => (newObj[key] !== value ? { ...acc, ...{ [key]: newObj[key] } } : acc),
[]
);
}
i have an object like:
const obj = {
obj2: {
number: 123123,
obj3: [{
number: 321321
}, {
number: 543543
}]
},
obj5: {
number: 52343,
obj6: [{
number: 753456
}, {
number: 12312
}]
},
}
wanted output: [123123, 321321, 543543, 52343, 753456, 12312]
the ways i'm resolving it is just..
creating an array like
[obj2.number, obj2.obj3[0].number, obj5.number, obj5.obj6[0].number].find(el => Boolean(el))
and it's looks bad. Can't figuring out how to do it in function way.
Like make Object entries of this object, then reduce it and ask for field, if it's not there, go deeper etc.
or the first value in field named 'number' which is not null (boolean true)
Not a fancy way and not guaranteed to work for each and every case, but you could start from here
const obj = {
obj2: {
number: 123123,
obj3: [{
number: 321321
}, {
number: 543543
}]
},
obj5: {
number: 52343,
obj6: [{
number: 753456
}, {
number: 12312
}]
},
}
getPropValues = function(obj, name) {
let result = [];
if(Array.isArray(obj)) {
for(let i =0;i<obj.length;++i) {
let innerResult = getPropValues(obj[i],name);
result = result.concat(innerResult);
}
return result;
}
if(typeof obj === 'object') {
for (const [key, value] of Object.entries(obj)) {
if(key === name) {
result.push(value);
} else if(Array.isArray(value) || typeof value === 'object') {
result = result.concat(getPropValues(value,name));
}
}
}
return result;
}
console.log(getPropValues(obj,'number'))
const res = Object.values(obj).reduce((acc, rec) => {
return [...acc, ...Object.values(rec)]
}, []).map((it) => {
return typeof it === 'number' ? it : it.map((item) => Object.values(item))
}).reduce((acc, rec) => {
return typeof rec === 'number' ? [...acc, rec] : [...acc, ...rec]
},[]).reduce((acc, rec) => {
return typeof rec === 'number' ? [...acc, rec] : [...acc, ...rec]
},[])