const ob = {
a: 1,
b: {
c: 3,
d: 6,
e: {
f: {
g: 3,
h: {
i: 5,
j: {
k: 7
}
}
}
}
}
};
Any methods to solve this code?
I have no idea how to solve this code.
For abovementioned input I would expect a result of 1 + 3 + 6 + 3 + 5 + 7 = 25. So what I want to return from a function sumObject(ob) is: 25
You can try reduce with recursion
The condition for the sum is
If the current value is a number, sum it with result
If the current value is not a number (in your case, it's an object), call the function sumObject recursively with current result
const ob = {
a: 1,
b: {
c: 3,
d: 6,
e: {
f: {
g: 3,
h: {
i: 5,
j: {
k: 7
}
}
}
}
}
};
function sumObject(data, result = 0) {
return Object.values(data).reduce((sum, value) => typeof value === 'number' ? sum + value : sumObject(value, sum), result)
}
console.log(sumObject(ob))
If you don't understand some of the other answers, this is an easier solution to understand:
function sumObject(obj){
let result = 0;
for(let i of Object.values(obj)){ //we iterate through all values in obj
if(typeof i == "number"){ //if the current value of i is a number
result+=i; //then add that to the result
} else { //otherwise, it will be an object
result+=sumObject(i); //so we call the function on itself to iterate over this new object
}
}
return result; //finally, we return the total
}
console.log(sumObject(ob));
You can do this to extract all values into an array of numbers and then sum that array:
const getObjectValues = (obj) => (obj && typeof obj === 'object')
? Object.values(obj).map(getObjectValues).flat()
: [obj]
let nums = getObjectValues(ob)
let sum = nums.reduce((a, b) => a + b, 0)
You can recursively sum the value of each object using array#reduce and Object.values()
const ob = { a: 1, b: { c: 3, d: 6, e: { f: { g: 3, h: { i: 5, j: { k: 7 } } } } } },
getSum = o =>
Object.values(o).reduce((s, v) => {
s += typeof v === 'object' ? getSum(v): v;
return s;
}, 0);
console.log(getSum(ob));
Try recursion. This will support any amount of nested object at any level of nesting.
const sumAllNumbers = (object, total = 0) => {
const nextValues = [];
for (const value of Object.values(object)) {
if (typeof value === 'number') total += value;
if (typeof value === 'object') nextValues.push(Object.values(value));
}
if (!nextValues.length) return total;
return sumAllNumbers(nextValues.flat(), total);
};
// adds up to 25
const testCase1 = {
a: 1,
b: {
c: 3,
d: 6,
e: {
f: {
g: 3,
h: {
i: 5,
j: {
k: 7,
},
},
},
},
},
};
// adds up to 30
const testCase2 = {
a: 1,
b: 2,
c: {
a: 1,
b: 2,
c: 3,
d: {
a: 5,
b: {
c: {
d: {
e: 1,
},
},
},
},
},
d: {
g: {
f: {
c: 10,
},
},
},
e: {
f: {
a: 1,
g: {
a: 4,
},
},
},
};
// Your original test case
console.log(sumAllNumbers(testCase1));
// My much more demanding test case
console.log(sumAllNumbers(testCase2));
You could get the value of the objects and check if the value is an object, then take the result of the nested objects or call the handed over sum function for accumulator and actual value.
This approach works with a function which takes
an object
an accumulator function
a start value
This function works as well for getting all values with different accumulator function and an array as startValue.
const
reduce = (object, fn, startValue) => Object
.values(object)
.reduce(
(r, v) => v && typeof v === 'object' ? reduce(v, fn, r) : fn(r, v),
startValue
),
data = { a: 1, b: { c: 3, d: 6, e: { f: { g: 3, h: { i: 5, j: { k: 7 } } } } } },
total = reduce(data, (a, b) => a + b, 0);
console.log(total);
const ob = {
a: 1,
b: {
c: 3,
d: 6,
e: {
f: {
g: 3,
h: {
i: 5,
j: {
k: 7
}
}
}
}
}
};
const sum = data =>
Object
.keys(data)
.reduce((a,b) => a + (typeof(s = data[b]) == "number" ? s : sum(s)), 0);
console.log(sum(ob))
Related
I have this nested Object:
{
1: { one: { a: 5, b: 6, c: 7, d: 8 } },
2: { one: { a: 6, b: 9, c: 10, d: 12, e: 1 } },
3: { one: { a: 3, b: 4, c: 9 } },
}
Required output:
{
one: {
a: [5, 6, 3]
b: [6, 9, 4]
c: [7, 10, 9]
d: [12]
e: [1]
}
}
Tried nested queries but failed.
Here's my take. A series of nested for for...in loops. Tried to optimize for readability.
const object = {
1: { one: { a: 5, b: 6, c: 7, d: 8 } },
2: { one: { a: 6, b: 9, c: 10, d: 12, e: 1 } },
3: { one: { a: 3, b: 4, c: 9 } },
};
const result = {};
for (const group in object) {
for (const num in object[group]) {
if (!result[num]) {
result[num] = {};
}
for (const letter in object[group][num]) {
if (!result[num][letter]) {
result[num][letter] = [];
}
result[num][letter].push(object[group][num][letter])
}
}
}
console.log(result)
Use nested loops. You don't care about the keys of the top-level object, so loop over its values with Object.values(). Then loop over the keys and values of the nested objects. Create nested objects in the result when needed, then add the values.
const obj1 = {
1: {one:{ a:5,b:6,c:7,d:8}},
2: {one:{ a:6,b:9,c:10,d:12,e:1}},
3: {one:{a:3,b:4,c:9}}
};
let result = {};
Object.values(obj1).forEach(val1 => {
Object.entries(val1).forEach(([key2, val2]) => {
if (!result.hasOwnProperty(key2)) {
result[key2] = {};
}
res2 = result[key2];
Object.entries(val2).forEach(([key3, val3]) => {
if (!res2.hasOwnProperty(key3)) {
res2[key3] = [val3];
} else {
res2[key3].push(val3);
}
});
});
});
console.log(result);
My approach is doing two groups:
group the first top-level object keys and get their values store them as value of an array.
group the keys of the arrays value (in the previous group)
let objs = {
1: { one: { a: 5, b: 6, c: 7, d: 8 } },
2: { one: { a: 6, b: 9, c: 10, d: 12, e: 1 } },
3: { one: { a: 3, b: 4, c: 9 } },
};
let groupByKey = {};
for (const obj of Object.values(objs)) {
let [key, inerObjs] = Object.entries(obj)[0];
groupByKey[key] ??= [];
groupByKey[key].push(Object.entries(inerObjs));
}
let result = {};
for (const [k, arrs] of Object.entries(groupByKey)) {
let group = {};
for (const obj of arrs.flat()) {
if (!group.hasOwnProperty(obj[0])) {
group[obj[0]] = [obj[1]];
} else {
group[obj[0]].push(obj[1]);
}
}
result[k] = group;
}
console.log(result);
Here is a recursive option that works for objects of arbitrary depth. Here calling it with an empty object as target and spreading the Object.values of your input in order to skip the top level properties as per your expected output.
function groupProperties(target, ...objects) {
for (const obj of objects) {
for (const [k, v] of Object.entries(obj)) {
if (typeof v === 'object') {
groupProperties((target[k] ??= {}), v);
}
else {
(target[k] ??= []).push(v);
}
}
}
return target;
}
const input = { 1: { one: { a: 5, b: 6, c: 7, d: 8 } }, 2: { one: { a: 6, b: 9, c: 10, d: 12, e: 1 } }, 3: { one: { a: 3, b: 4, c: 9 } }, };
const result = groupProperties({}, ...Object.values(input));
console.log(result);
const obj1 = {
a: 5,
e: {
c: 10,
l: {
b: 50,
},
},
};
const obj2 = {
a: 5,
e: {
c: 10,
},
};
need to get ['l', 'b'] or maybe not in the array
Here's a recursive function that deep compares the keys of both objects. This also takes into account the structure and nesting of the children.
So essentially, it goes through each nested key in obj1 and makes sure that there's an equivalent key in the same location in obj2
Your example
const obj1 = {
a: 5,
e: {
c: 10,
l: {
b: 50,
},
},
};
const obj2 = {
a: 5,
e: {
c: 10,
},
};
const missingKeys = []
function compare(obj1, obj2) {
for (let prop in obj1) {
if (obj2[prop]) {
if (typeof obj1[prop] === 'object' && typeof obj2[prop] === 'object') {
compare(obj1[prop], obj2[prop])
}
} else {
if (typeof obj1[prop] === 'object') {
compare(obj1[prop], {})
}
missingKeys.push(prop)
}
}
}
compare(obj1, obj2)
console.log(missingKeys)
Example 2:
const obj1 = {
a: 5,
e: {
c: 10,
l: {
b: 50,
d: 20,
},
},
z: 50
};
const obj2 = {
a: 5,
e: {
c: 10,
},
b: 50, // shares same key name but nested in different location
l: 50, // also shares same key but nested differently
z: 50,
};
const missingKeys = []
function compare(obj1, obj2) {
for (let prop in obj1) {
if (obj2[prop]) {
if (typeof obj1[prop] === 'object' && typeof obj2[prop] === 'object') {
compare(obj1[prop], obj2[prop])
}
} else {
if (typeof obj1[prop] === 'object') {
compare(obj1[prop], {})
}
missingKeys.push(prop)
}
}
}
compare(obj1, obj2)
console.log(missingKeys)
This would work. Without checking the levels and assuming the unique field names.
const obj1 = {
a: 5,
e: {
c: 10,
l: {
b: 50,
},
},
};
const obj2 = {
a: 5,
e: {
c: 10,
},
};
const getAllKeys = (obj) => {
let keyNames = Object.keys(obj);
Object.values(obj).forEach((value) => {
if (typeof value === "object") {
keyNames = keyNames.concat(getAllKeys(value));
}
});
return keyNames;
};
const getFilteredKeys = (keySet1, keySet2) =>
keySet1.filter((key) => !keySet2.includes(key));
const output = getFilteredKeys(getAllKeys(obj1), getAllKeys(obj2));
console.log(output);
I am writing a function for calculating the depth of an object.
Here is my recursive version which seems to work as expected:
function findDepth(obj, firstCall = true) {
if (firstCall && typeof obj !== "object") {
return -1;
}
return Object.keys(obj).reduce((max, k) => {
if (typeof obj[k] === "object" && obj[k] !== null) {
const val = findDepth(obj[k], false) + 1;
if (val > max) {
max = val;
}
}
return max;
}, 1);
}
const input1 = {
a: {
b: "test",
c: {
d: {
e: {
f: [1, 2, 3],
g: {
a: null,
z: {
d: "casdsadasdsa",
q: {
z: {
i: undefined
}
}
}
}
}
},
c: {
a: "sad"
}
},
d: {
e: 5
}
},
b: {
c: {
d: "dsada"
}
}
};
const input2 = {
w: {
d: "hello",
f: {
g: "dsadas",
z: {
b: "dsajkdasjldk",
q: {
w: {
z: "dsajkdasjdla"
}
}
},
h: "dsiaodsiad"
}
},
a: "test",
b: "test2",
c: {
d: "hello",
f: {
g: "dsadas",
z: {
b: "dsajkdasjldk"
},
h: "dsiaodsiad"
}
},
e: "bye"
};
console.log(findDepth(input1));
console.log(findDepth(input2));
Now I am trying to write an iterative version, but I cannot find the best way to make the loop work.
function findDepthIterative(obj) {
if (typeof obj !== "object") {
return -1;
}
let max = 1;
let copy = Object.assign({}, obj);
let keys = Object.keys(copy);
while (keys.length) {
if (typeof copy[keys[0]] !== "object" && copy[keys[0]] !== null) {
delete copy[keys[0]];
keys = Object.keys(copy);
} else {
max++;
copy = Object.assign({}, copy[keys[0]]);
keys = Object.keys(copy);
}
}
return max;
}
const input1 = {
a: {
b: "test",
c: {
d: {
e: {
f: [1, 2, 3],
g: {
a: null,
z: {
d: "casdsadasdsa",
q: {
z: {
i: undefined
}
}
}
}
}
},
c: {
a: "sad"
}
},
d: {
e: 5
}
},
b: {
c: {
d: "dsada"
}
}
};
const input2 = {
w: {
d: "hello",
f: {
g: "dsadas",
z: {
b: "dsajkdasjldk",
q: {
w: {
z: "dsajkdasjdla"
}
}
},
h: "dsiaodsiad"
}
},
a: "test",
b: "test2",
c: {
d: "hello",
f: {
g: "dsadas",
z: {
b: "dsajkdasjldk"
},
h: "dsiaodsiad"
}
},
e: "bye"
};
console.log(findDepthIterative(input1));
console.log(findDepthIterative(input2));
As you can see from the output and the code, it just takes the first property inside the loop:
while (keys.length) {
if (typeof copy[keys[0]] !== "object" && copy[keys[0]] !== null) {
delete copy[keys[0]];
keys = Object.keys(copy);
} else {
max++;
copy = Object.assign({}, copy[keys[0]]);
keys = Object.keys(copy);
}
}
The idea was to delete the property each iteration, but I am not getting in the right way.
I tried to change it with copy[keys[keys.length - 1]] but in this way it takes only the last property instead.
Actually the issue is how to loop over all the keys, on all the depth levels, as in the recursive version.
Any suggestion about how to implement this algorithm in an iterative way?
Even any suggestion on how to improve the recursive one (or if I am missing something) is more than welcome.
p.s. NO LOADASH, UNDERSCORE, RAMDA, or whatever. Just Vanilla JS
You just need to keep a stack and push children into it while keeping track of the current depth. You can keep track of that by pushing an array of [depth, obj] into the stack and then when you pop() add one to the depth before pushing children.
const input1 = {w: {d: "hello",f: {g: "dsadas",z: {b: "dsajkdasjldk",q: {w: {z: "dsajkdasjdla"}}},h: "dsiaodsiad"}},a: "test",b: "test2",c: {d: "hello",f: {g: "dsadas",z: {b: "dsajkdasjldk"},h: "dsiaodsiad"}},e: "bye"};
function findDepthIterative(obj) {
if (typeof obj !== "object") {
return -1;
}
let max = 0;
// current depth, children
let stack = [[0, Object.values(obj)]];
while(stack.length){
let [depth, cur] = stack.pop()
if (depth > max) max = depth
if (typeof cur === "object" && cur !== null){
Object.values(cur).forEach(c => stack.push([depth+1, c]))
}
}
return max
}
console.log(findDepthIterative(input1))
// sanity check:
const depth0 = {}
const depth1 = {a:1}
const depth2 = {a:{b:2}}
console.log(findDepthIterative(depth0))
console.log(findDepthIterative(depth1))
console.log(findDepthIterative(depth2))
One way to iterate could be a depth first search using a stack.
function f(obj){
let stack = Object.keys(obj).map(k => [obj[k], 1]);
let max = 0;
while (stack.length){
let [maybeObj, d] = stack.pop();
max = Math.max(max, d);
if (typeof maybeObj == 'object' && maybeObj !== null)
Object.keys(maybeObj).map(k =>
stack.push([maybeObj[k], d + 1]));
}
return max;
}
We could also make the recursion slightly more succinct:
function f(obj){
if (typeof obj !== 'object' || obj === null)
return 0;
return Object.keys(obj).reduce((acc, k) =>
Math.max(acc, 1 + f(obj[k])), 0);
}
You should use an array instead of the object which won't work properly if there are duplicate keys. The array should contain all the objects that occur at a certain level. For each iteration, you map the array into a new one containing the previous objects' direct children:
function findDepthIterative(obj) {
if (typeof obj !== "object") {
return -1;
}
let arr = [obj]; // the array containing the objects that occur at a certain level, initially contains obj being the only object at the first level
let levels = 0; // levels counter
do { // keep doing this
levels++; // increment the levels counter
let newArr = []; // make a new array for the next level
arr.forEach(obj => { // populate it with the old level objects' children
for(let key in obj) {
if(obj[key] && typeof obj[key] === "object") {
newArr.push(obj[key]);
}
}
});
arr = newArr; // make arr the new array of object (next level)
} while (arr.length); // while there are still levels with objects in them
return levels;
}
Demo:
function findDepthIterative(obj) {
if (typeof obj !== "object") {
return -1;
}
let arr = [obj];
let levels = 0;
do {
levels++;
let newArr = [];
arr.forEach(obj => {
for(let key in obj) {
if(obj[key] && typeof obj[key] === "object") {
newArr.push(obj[key]);
}
}
});
arr = newArr;
} while (arr.length);
return levels;
}
const input1 = {
a: {
b: "test",
c: {
d: {
e: {
f: [1, 2, 3],
g: {
a: null,
z: {
d: "casdsadasdsa",
q: {
z: {
i: undefined
}
}
}
}
}
},
c: {
a: "sad"
}
},
d: {
e: 5
}
},
b: {
c: {
d: "dsada"
}
}
};
const input2 = {
w: {
d: "hello",
f: {
g: "dsadas",
z: {
b: "dsajkdasjldk",
q: {
w: {
z: "dsajkdasjdla"
}
}
},
h: "dsiaodsiad"
}
},
a: "test",
b: "test2",
c: {
d: "hello",
f: {
g: "dsadas",
z: {
b: "dsajkdasjldk"
},
h: "dsiaodsiad"
}
},
e: "bye"
};
console.log(findDepthIterative(input1));
console.log(findDepthIterative(input2));
Suppose I have some object like:
const someObj = {
x: null,
y: {
z: null
},
a: {
b: {
c: null
}
}
}
I would like to create a function to set values using something like:
const setKV = (obj, ...keyArray) => {
/* Not quite sure how to phrase this function */
const val = keyArray.pop()
}
Such that I can set the values:
x with setKV(someObj, 'x', true)
z with setKV(someObj, 'y', 'z', true)
c with setKV(someObj, 'a', 'b', 'c', true)
How would I define an object's nested key by this arbitrary number of parameters?
You can do this easily using a rest parameter and spread argument like ...rest below. setKV does not mutate its input object, o; a new object is always returned.
const setKV = (o = {}, key, value, ...rest) =>
rest.length === 0
? { ...o, [key]: value }
: { ...o, [key]: setKV (o[key], value, ...rest) }
console.log
( setKV ({ a: 0 }, 'b', 1)
// { a: 0, b: 1 }
, setKV ({ a: { b: { c: false } } }, 'a', 'b', 'c', true)
// { a: { b: { c: true } } }
, setKV ({}, 'a', 'b', 'c', 1)
// { a: { b: { c: 1 } } }
, setKV ({ a: { b: { c: 0 } } }, 'a', 'b', 'd', 0)
// { a: { b: { c: 0, d: 0 } } }
, setKV ({ a: { b: { c: 0 } } }, 'a', 'b', 1)
// { a: { b: 1 } }
, setKV ({ a: 1, b: { c: 2, d: { e: 3 } } }, 'b', 'd', 'e', { f: 4 })
// { a: 1, b: { c: 2, d: { e: { f: 4 } } } }
, setKV ({ a: 0 }, 'b')
// { a: 0, b: undefined }
)
"If I did want to mutate the input object ..."
While mutations should be avoided, the specific needs of your program may warrant their use. In such a case, review mutKV, if only to see how it differs from the implementation above
const mutKV = (o = {}, key, value, ...rest) =>
rest.length === 0
? (o[key] = value, o)
: (o[key] = mutKV (o[key], value, ...rest), o)
const data =
{ a: 0 }
mutKV (data, 'b', 1)
console.log (data)
// { a: 0, b: 1 }
mutKV (data, 'c', 'd', 2)
console.log (data)
// { a: 0, b: 1, c: { d: 2 } }
mutKV (data, 'c', 'd', 3)
console.log (data)
// { a: 0, b: 1, c: { d: 0 } }
mutKV (data, 'c', 4)
console.log (data)
// { a: 0, b: 1, c: 4 }
This opens the book for a short lesson about side effects, and encoding them using effect. Below we create an effect mut using effect, then mut is used in each branch of mutKV. The behavior of the program is identical to mutKV above.
const effect = f => x =>
(f (x), x)
const mut = (key, value) =>
effect (o => o[key] = value)
const mutKV = (o = {}, key, value, ...rest) =>
rest.length === 0
? mut (key, value) (o)
: mut (key, mutKV (o[key], value, ...rest)) (o)
lodash provides what you're looking for, no need to rewrite it
https://lodash.com/docs/4.17.10#set
_.set(someObj, ['a', 'b', 'c'], true);
We can create function where rest params are two element arrays where first item is key name and secound is desired value
function setKV(obj, ...kvs) {
return kvs.reduce((obj, [k, v]) => {
obj[k] = v;
return obj;
}, obj);
}
function setKV(obj, ...kvs) {
return kvs.reduce((obj, [k, v]) => {
obj[k] = v;
return obj;
}, obj);
}
console.log(setKV({}, [
"a",
1
], [
"b",
"b"
]));
I want to have something like this: getting another object without some properties. I already have a working solution:
var s = {
a: 3,
b: 2,
c: -1,
d: 8,
e: -1
};
var f = {};
jQuery.map(s, function(v,k) {
if (v != -1)
{
f[k] = v;
}
});
output is:
a: 3,
b: 2,
d: 8,
its fine but isnt there more simpler sollution? Maybe with jQuery but neither .map, .filter, .grep methods helped me so far!
This may help
var s = {
a: 3,
b: 2,
c: -1,
d: 8,
e: -1
};
for(var k in s){
if(s[k] == -1){
delete(s[k]);
}
}
console.log(s);
You can so something like this
var s = {
a: 3,
b: 2,
c: -1,
d: 8,
e: -1
};
for (var property in s) {
if (s[property]!=-1) {
console.log(property+":"+s[property]);
}
}
You could use delete operator for deleting a property of an object.
var s = { a: 3, b: 2, c: -1, d: 8, e: -1 }
Object.keys(s).forEach(function (k) {
if (s[k] === -1) {
delete s[k];
}
});
console.log(s);