I am trying to learn how to cope with the objects and arrays and I saw many ways of iterating objects but recursing doesn't work for me and I don't understand why. What am I doing wrong?
I need to loop through an object and just slightly change something in an array. In my case, it's uppercasing the keys
Here is what I've got for now
const development = {
port: 8080,
db: {
username: "jkdfs",
password: "dsfsdg",
name: "kslfjskd",
test: { test: 12, another: 'value' }
},
token_secret: "jfkjdsj",
hash_rounds: "kjsfkljdfkl"
};
function throughObject(obj) {
let collection = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
let value = obj[key];
if (typeof obj[key] !== 'object') {
collection[key.toUpperCase()] = value;
} else {
collection[key.toUpperCase()] = nestedObject(obj[key]);
}
}
function nestedObject(nested) {
const sub = {};
for (const k in nested) {
let v = nested[k];
if (typeof nested[k] !== 'object') {
sub[k.toUpperCase()] = v;
} else {
nestedObject(v);
}
}
return sub;
}
}
return collection;
}
const r = throughObject(development);
console.log(r);
When you're recursively calling the function on an object value, you still need to assign it to the sub object: sub[k.toUpperCase()] = nestedObject(v). Also, you don't need 2 different functions.
const development = {
port: 8080,
db: {
username: "jkdfs",
password: "dsfsdg",
name: "kslfjskd",
test: { test: 12, another: 'value' }
},
token_secret: "jfkjdsj",
hash_rounds: "kjsfkljdfkl"
};
function nestedObject(nested) {
const sub = {};
for (const k in nested) {
const v = nested[k];
if (typeof nested[k] !== 'object')
sub[k.toUpperCase()] = v;
else
sub[k.toUpperCase()] = nestedObject(v); // <- HERE
}
return sub;
}
console.log(nestedObject(development))
Here's a shorter version using Object.fromEntries()
const development={port:8080,db:{username:"jkdfs",password:"dsfsdg",name:"kslfjskd",test:{test:12,another:"value"}},token_secret:"jfkjdsj",hash_rounds:"kjsfkljdfkl"};
const convert = o =>
Object.fromEntries(
Object.entries(o).map(([k, v]) =>
[k.toUpperCase(), Object(v) === v ? convert(v) : v]
)
)
console.log(convert(development))
Related
I have an array of object from which I am trying to get values using map operator but I am getting the whole json objects all I want is just array of values.
Below is my code:
const obj = [
{
a: {
b: 'Paul',
}
},
{
c: 'Byeeee',
}
];
obj.map((val) => console.log(val));
what I am getting is
{ a: { b: 'Paul' } }
{ c: 'Byeeee' }
What I want is:
['Paul','Byeeee']
Someone let me know how can I get the desired output.
You can do this recursively. You can first start off by grabbing the values of your object, and then loop through those using .flatMap(). If you encounter a value that is an object, you can recursively grab the values of that object by recalling your function. Otherwise, you can return the value. The advantage of using .flatMap() here is that when the recursive call returns an array, we don't end up with inner arrays, but rather the array gets flattened into one resulting array:
const obj = [{ a: { b: 'Paul', } }, { c: 'Byeeee', } ];
const getValues = (obj) => {
return Object.values(obj).flatMap(val => Object(val) === val ? getValues(val) : val);
}
console.log(getValues(obj));
you can use the following solution.
const data = [{ a: { b: 'Paul' } }, { c: 'Byeeee' }];
const flatObjectValues = (obj, result) => {
// recursive function to get object values
const objValues = Object.values(obj);
if (objValues?.length > 0) {
objValues.map((v) => {
if (typeof v === 'object' && !Array.isArray(v)) {
flatObjectValues(v, result);
} else {
result.push(v);
}
return v;
});
}
};
const updatedData = [];
data.map((x) => flatObjectValues(x, updatedData));
console.log('updatedData: ', updatedData);
You can use recursion with array.reduce, like fellowing.
function getAllValues(objuct) {
return objuct.reduce((acc, curr) => {
if (typeof curr === 'object') {
return [...acc, ...getAllValues(Object.values(curr))];
}
return [...acc, curr];
}, []);
}
A recursive solution could be:
const arr = [{a: {b: "Paul",},},{c: "Byeeee",},];
const flatArrOfObjects = (arr) => {
const values = [];
for (const i in arr) flatObj(arr[i], values);
return values;
};
const flatObj = (obj, result) => {
for (const [key, value] of Object.entries(obj)) {
if (typeof value === "object") flatObj(value, result);
else result.push(value);
}
};
console.log(flatArrOfObjects(arr));
I want to be able to retrieve the value of a property if it exists anywhere within the object graph and I would like to know if there is a faster way to the option I have come up with below.
I have specifically opted for tree traversal instead of recursion, because it uses less stack frames and memory and because traversal is faster.
example:
const obj= {someObj:{}, someOtherObj: { nestedObjet: {featureRole: "canary"}}}
const prop= findProperties(obj, "featureRole");
console.log(prop); //canary
breadth first search properties
findProperty(obj: Record<string, any>, propId: string): any {
if (obj && obj[propId] != undefined) {
return obj[propId];
}
const props = [obj];
while (props.length) {
const prop = props.shift();
if (typeof prop === 'object') {
if (prop && prop[propId] != undefined) {
return prop[propId];
}
for (const innerProp in prop) {
if (Object.prototype.hasOwnProperty.call(prop, innerProp) && typeof prop[innerProp] === 'object') {
props.push(prop[innerProp]);
}
}
}
}
You can do this recursively, either returning as soon as a value is found:
const obj = {
someObj: {}, someOtherObj: { nestedObjet: { featureRole: "canary" } },
obj: { someOtherObj: { nestedObjet: { featureRole: "cockatoo" } } }
}
const findProperties = (obj, prop) => {
if (obj[prop] !== undefined) {
return obj[prop];
}
for (const key in obj) {
if (typeof obj[key] === 'object') {
const res = findProperties(obj[key], prop);
if (res !== null) {
return res
}
}
}
return null;
}
const prop = findProperties(obj, "featureRole");
console.log(prop); //canary
Or collecting all the possible matches:
const obj = {
someObj: {}, someOtherObj: { nestedObjet: { featureRole: "canary" } },
obj: { someOtherObj: { nestedObjet: { featureRole: "cockatoo" } } }
}
const findProperties = (obj, prop, res = []) => {
if (obj[prop] !== undefined) {
res.push(obj[prop]);
}
for (const key in obj) {
if (typeof obj[key] === 'object') {
findProperties(obj[key], prop, res);
}
}
return res;
}
const prop = findProperties(obj, "featureRole");
console.log(prop); // ["canary", "cockatoo"]
Solution with some restrictions like \" in property name/value:
findProperties = (o, prop) => JSON.stringify(o).match(new RegExp(`"\(${prop}\)":"\([^"]*\)"`, 'gi')).map(m => m.split(':').map(str => str.replace(/"/gi, '')));
Usage:
findProperties(obj, "featureRole").map(([,v]) => console.log(v));
You can use recursivity to search in your object.
const object = {
a: {
d: 24
},
b: {
e: 5,
f: {
g: 'B'
}
},
c: 12
};
function getProp(object, propName) {
if (propName in object) {
return object[propName];
} else {
for (const prop in object) {
if (typeof object[prop] === 'object') {
const results = getProp(object[prop], propName);
if(!!results) {
return results;
}
}
}
}
}
You can play with it here: https://jsfiddle.net/tpbhexgw/10/
Here is an iterative solution using object-scan.
This library is build from the ground up for speed. It uses iteration instead of recursion internally.
// const objectScan = require('object-scan');
const obj = { someObj: {}, otherObj: { some: { deep: { nested: { key: {} } } } }, someOtherObj: { nestedObjet: { featureRole: 'canary' } } };
// compile search function (only needs to be done once)
const searchFn = objectScan(['**.featureRole'], { abort: true, rtn: 'bool' });
// execute search
console.log(searchFn(obj));
// => true
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#14.0.0"></script>
Disclaimer: I'm the author of object-scan
While this will not be faster than writing your own iterative solution, it has the major advantage that it will prune the search tree if you have any other meta information about the key position.
For example if you knew that the key is at exactly one or two nestings deep, you could write the following and it would never search deeper than that
// const objectScan = require('object-scan');
const obj = { someObj: {}, otherObj: { some: { deep: { nested: { key: {} } } } }, someOtherObj: { nestedObjet: { featureRole: 'canary' } } };
const searchFn = objectScan(['{*,*.*}.featureRole'], { abort: true, rtn: 'bool' });
console.log(searchFn(obj));
// => true
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#14.0.0"></script>
Disclaimer: I'm the author of object-scan
These kind of changes have typically a much greater impact than micro-optimizing code (that a different compiler might do who knows what with anyways).
I have an JS array of objects as below:
[
{
key1:"value1",
key2:"value2",
key3:"value3"
},
{
key1:"value1",
key2_updated:"value2_updated",
key3_updated:"value3_updated"
}
]
Post transformation , it must be of type object as below:
{
value1: {
key2:"value2",
key3:"value3",
key2_updated:"value2_updated",
key3_updated:"value3_updated"
}
}
Notice In array , first key-value of each object is same and hence i wanted to merge them and create new object with value1:mergecontent and if it is not same ,, then create new k-v in same object.
Group the array of object by key1 first, then assign the attributes
var data = [
{
key1:"value1",
key2:"value2",
key3:"value3"
},
{
key1:"value1",
key2_updated:"value2_updated",
key3_updated:"value3_updated"
},
{
key1:"value2",
key2_updated:"value2_updated",
key3_updated:"value3_updated"
}
];
var result = data.reduce((x, v) => {
x[v.key1] = x[v.key1] || {}
const { key1, ...res } = v
x[v.key1] = Object.assign(x[v.key1], res)
return x
}, {})
var newResult = data.reduce((x, v) => {
x[v.key1] = x[v.key1] || {}
const { key1, ...res } = v
var newObj = {}
var currentKey
for (let [key, value] of Object.entries(res)) {
if (currentKey) {
newObj[currentKey] = value;
currentKey = null;
} else {
newObj[value] = "";
currentKey = value
}
}
x[v.key1] = Object.assign(x[v.key1], newObj)
return x
}, {})
function transform(d, k){
return d.reduce((x, v) => {
x[v[k]] = x[v[k]] || {}
const { key1, ...res } = v
var newObj = {}
var currentKey
for (let [key, value] of Object.entries(res)) {
if (currentKey) {
newObj[currentKey] = value;
currentKey = null;
} else {
newObj[value] = "";
currentKey = value
}
}
x[v[k]] = Object.assign(x[v[k]], newObj)
return x
}, {})
}
console.log(transform(data, "key1"))
As a part of a challenge I need to implement the .findKey() method myself. Below is the solution proposition, however, I get an error "predicate is not a function".
const _ = {
findKey(object, predicate) {
for (let key in object) {
let value = object[key];
let predicateReturnValue = predicate(value);
if (predicateReturnValue) {
return key;
};
};
undefined
return undefined;
}
};
Can anyone help?
function findKey(object, predicate) {
for (let key in object) {
let value = object[key];
let predicateReturnValue = predicate(value);
if (predicateReturnValue) { // just take the value
return key; // return key
}
}
}
const isEqual = a => b => a === b
const object = { a: 'Apple', b: 'Beer', c: 'Cake' }
alert(findKey(object, isEqual('Apple')));
alert(findKey(object, isEqual('Cakes')));
Found this excellent code for _.get vanilla js implementation:
const get = (obj, path, defaultValue) => path.split(".")
.reduce((a, c) => (a && a[c] ? a[c] : (defaultValue || null)), obj)
Now I'm looking for _.set implementation, any help would be appreciated.
I think this could cover it:
const set = (obj, path, value) => {
if (Object(obj) !== obj) return obj; // When obj is not an object
// If not yet an array, get the keys from the string-path
if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || [];
path.slice(0,-1).reduce((a, c, i) => // Iterate all of them except the last one
Object(a[c]) === a[c] // Does the key exist and is its value an object?
// Yes: then follow that path
? a[c]
// No: create the key. Is the next key a potential array-index?
: a[c] = Math.abs(path[i+1])>>0 === +path[i+1]
? [] // Yes: assign a new array object
: {}, // No: assign a new plain object
obj)[path[path.length-1]] = value; // Finally assign the value to the last key
return obj; // Return the top-level object to allow chaining
};
// Demo
var obj = { test: true };
set(obj, "test.1.it", "hello");
console.log(obj); // includes an intentional undefined value
It is a bit more complex than get, because there is some logic needed to create missing parts of the path in the object, to overwrite primitive values that stand in the way, and to determine whether a new child should better be an array or a plain object.
Check this one:
/**
* #example
* const obj = {id:1, address: {city: 'Minsk', street: 'Prityckogo 12'}}
* setByString(obj, 'address.city', 'Grodno'); obj.address.city => 'Grodno'
* setByString(obj, ['address', 'city'], 'Grodno'); obj.address.city => 'Grodno'
* setByString(obj, ['address', 'city', 'phones'], {mobile: 1234, home: 222}); obj.address.city.phones.home => 222
*/
/**
* #param {any} input
* #return {boolean}
*/
const isObject = (input) => (
null !== input &&
typeof input === 'object' &&
Object.getPrototypeOf(input).isPrototypeOf(Object)
)
**/
* #param {object} obj
* #param {string} path
* #param {any} value
*/
const setByString = (obj, path, value) => {
const pList = Array.isArray(path) ? path : path.split('.');
const len = pList.length;
// changes second last key to {}
for (let i = 0; i < len - 1; i++) {
const elem = pList[i];
if (!obj[elem] || !isObject(obj[elem])) {
obj[elem] = {};
}
obj = obj[elem];
}
// set value to second last key
obj[pList[len - 1]] = value;
};
const set = (obj = {}, paths = [], value) => {
const inputObj = obj === null ? {} : { ...obj };
if (paths.length === 0) {
return inputObj;
}
if (paths.length === 1) {
const path = paths[0];
inputObj[path] = value;
return { ...inputObj, [path]: value };
}
const [path, ...rest] = paths;
const currentNode = inputObj[path];
const childNode = set(currentNode, rest, value);
return { ...inputObj, [path]: childNode };
};
Example:
const input = {};
set(input, ['a', 'b'], 'hello');
Result:
{ a: { b: 'hello' }}
Here's an implementation of lodash 'get' and 'set' without using dot or bracket notation; useful for passing security scans.
https://jsfiddle.net/5amtL8zx/17/
/* lodash implementation of 'get', 'set', and 'unset' without dot or bracket notation
* - supports getting and setting 'prop1.2' array element but not with brackets: 'prop1.[2]'
*/
isObjectKey = (obj, key) => {
return Object.getPrototypeOf(obj) === Object.prototype && /string|number/.test(typeof key);
}
isArrayNumber = (obj, key) => {
const isKey = /string|number/.test(typeof key), path = isKey ? String(key).split('.') : [], prop = isKey && path.length > 1 ? path.shift() : '';
return Object.getPrototypeOf(obj) === Array.prototype && isKey && !isNaN(prop);
}
isValid = (obj, key) => {
const isObj = isObjectKey(obj, key), isArr = isArrayNumber(obj, key);
return isObj || isArr;
}
define = (obj, key, value) => {
Object.defineProperty(obj, String(key), { value, writable: true, configurable: true, enumerable: true });
}
get = (obj, key, value) => {
if (!isValid(obj, key)) {
return undefined;
}
let path = String(key).split('.'), prop = path.shift(), result = new Map(Object.entries(obj)).get(prop);
return path.length && typeof result !== 'undefined' ? get(result, path.join('.'), value) : result || value;
}
set = (obj, key, value) => {
if (!isValid(obj, key)) {
return undefined;
}
let path = key.split('.'), prop = path.shift();
if (!(prop in obj)) {
define(obj, prop, {});
}
const result = get(obj, prop);
return path.length && isValid(result, path.join('.')) ? set(result, path.join('.'), value) : define(obj, prop, value);
}
unset = (obj, key) => {
if (!isValid(obj, key)) {
return undefined;
}
let path = key.split('.'), prop = path.shift();
if (!(prop in obj)) {
return undefined;
}
if (path.length) {
let result = get(obj, prop);
result = unset(result, path.join('.'));
set(obj, prop, result);
return obj;
} else {
const { [prop]: remove, ...rest } = obj;
return rest;
}
}
let obj = {};
set(obj, 'prop1.prop2', 'value1');
console.log(Object.entries(obj));
console.log(get(obj, 'prop1.prop2'));
const prop1 = get(obj, 'prop1');
set(prop1, 'prop2', 'value2');
console.log(get(obj, 'prop1.prop2'));
set(obj, 'prop3', [1, 2, 3]);
console.log(get(obj, 'prop3'));
console.log(get(obj, 'prop3.2'));
console.log(get(obj.prop3, 0));
set(obj, 'prop3.3', 4);
console.log(get(obj, 'prop3.3'));
set(obj, 'prop4', [{'name': 'Bob'}]);
console.log(get(obj, 'prop4.0'));
unset(obj, 'prop4.0.name')
console.log(get(obj, 'prop4.0'));
//[["prop1", {
// prop2: "value1"
//}]]
//"value1"
//"value2"
//[1, 2, 3]
//3
//1
//4
//{
// name: "Bob"
//}
//{ ... }