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);
Related
How can we compare two array of objects on the basis of their keys or properties of object in javaScript?
for an example:
let result1 = [
{ a: 10, b: 20, c: 22 },
{ a: 20, b: 33, c: 11 },
];
let result2 = [
{ a: 10, b: 20 },
{ a: 20, b: 33 },
];
result1.filter(function (obj) {
return !result2.some(function (obj2) {
let key1 = Object.keys(obj);
let key2 = Object.keys(obj2);
key1?.forEach((x, index1) => {
key2?.forEach((y, index2) => {
console.log(index1, index2)
if (x === y) {
return obj[x] === obj2[y];
}
});
});
});
});
console.log(result1)
output: current output
expected output:
result1 =
[
{ a: 10, b: 20 },
{ a: 20, b: 33 },
];
I just try this solution in a different way, We can also achieve this requirement by doing deep copy of original array and then mutate it using forEach().
Live Demo :
let result1 = [
{ a: 10, b: 20, c: 22 },
{ a: 20, b: 33, c: 11 }
];
let result2 = [
{ a: 10, b: 20 },
{ a: 20, b: 33 }
];
const clone = structuredClone(result1);
clone.forEach((obj, index) => {
result1[index] = {};
Object.keys(result2[index]).forEach(key => {
result1[index][key] = obj[key]
});
});
console.log(result1);
let result1 = [
{ a: 10, b: 20, c: 22 },
{ a: 20, b: 33, c: 11 },
];
let result2 = [
{ a: 10, b: 20 },
{ a: 20, b: 33 },
];
let temp = []
result1.map(function(obj) {
return !result2.some(function(obj2) {
let key1 = Object.keys(obj);
let key2 = Object.keys(obj2);
key1.forEach((x) => {
key2.forEach((y) => {
if (x === y) {
obj[x] === obj2[y] ? temp.push({
[x]: obj[x]
}) : null;
}
})
});
})
})
console.log(temp);
try this code
You are getting the result1 array back beacause the array is not getting filtered.
The filter function is not getting anything from returned as the first foreach loop is not returning and the some function is also not returning anything
Now you can use map function instead of filter as the function is not returning anything
const filterKeysBasedOnFirstArrayOfObjects = <
T extends Record<string, unknown>
>(
arr1: T[],
arr2: T[]
) => {
const keys = _.intersection(
...arr1.map((obj) => Object.keys(obj)),
...arr2.map((obj) => Object.keys(obj))
);
return arr1.map((obj) => _.pick(obj, keys));
};
A more concise approach using lodash.
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))
I have list array object like:
let arr = [
{ a: 1, b: 2, c: 3, d: 4 },
{ a: 2, b: 3, c: 4, d: 5 },
{ a: 5, b: 6, c: 7, d: 8 }
]
and after using reduce()
// get props **b, c**
let arr_result = arr.reduce( ... )
// arr_result = [
// { b: 2, c: 3 },
// { b: 3, c: 4 },
// { b: 6, c: 7 }
// ]
use map.
let arr = [{
a: 1,
b: 2,
c: 3,
d: 4
},
{
a: 2,
b: 3,
c: 4,
d: 5
},
{
a: 5,
b: 6,
c: 7,
d: 8
}
]
const output = arr.map(({b, c}) => ({b, c}));
console.log(output);
You can use ES6(and beyond)'s object destructuring.
const arr = [
{ a: 1, b: 2, c: 3, d: 4 },
{ a: 2, b: 3, c: 4, d: 5 },
{ a: 5, b: 6, c: 7, d: 8 }
]
const res = arr.map(obj => {
const { b, c } = obj;
return { b, c };
});
console.log(res);
Since you asked to achieve this using reduce, here is the way. Pass an empty array as thisArg & inside reduce callback function create an object with required key and push it to the accumulator
let arr = [{
a: 1,
b: 2,
c: 3,
d: 4
},
{
a: 2,
b: 3,
c: 4,
d: 5
},
{
a: 5,
b: 6,
c: 7,
d: 8
}
];
let newArr = arr.reduce(function(acc, curr) {
acc.push({
b: curr.b,
c: curr.c
})
return acc;
}, [])
console.log(newArr)
If you want to use reduce:
const arr = [{a:1,b:2,c:3,d:4},{a:2,b:3,c:4,d:5},{a:5,b:6,c:7,d:8}];
const res = arr.reduce((a, { b, c }) => (a.push({ b, c }), a), []);
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: auto; }
It's honestly a lot easier with map:
const arr = [{a:1,b:2,c:3,d:4},{a:2,b:3,c:4,d:5},{a:5,b:6,c:7,d:8}];
const res = arr.map(({ b, c }) => ({ b, c }));
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: auto; }
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);
When working with data from database, we often get arrays of stuff that, due to database constraints, can be (uniquely) indexed by compound indices. However, indexBy does not seem to work for compound indices, or does it?
Given an array x with objects that have properties a and b, I want to have a dictionary of dictionaries that contain all objects of x, indexed by a and b, respectively. For example:
Fiddle here.
var x = [
{
a: 1,
b: 11,
c: 101
},
{
a: 2,
b: 11,
c: 101
},
{
a: 1,
b: 11,
c: 102
},
{
a: 1,
b: 14,
c: 102
},
];
// index x by a, then by b, then by c
var byABC = _.compoundIndexBy(x, ['a', 'b', 'c']);
// there are two items in `x` with a = 1 and b = 11
console.assert(_.size(byABC[1][11]) == 2, 'Something went wrong...');
// display result
console.log(byABC);
byABC now looks like this:
{
1: {
11: {
101: {
a: 1,
b: 11,
c: 101
},
102: {
a: 1,
b: 11,
c: 102
}
},
14: {
102: {
a: 1,
b: 14,
c: 102
}
},
}
2: {
11:{
101: {
a: 2,
b: 11,
c: 101
}
}
}
}
This Fiddle demonstrates the compoundexIndexBy function. Is my work in vain (because Lo-Dash actually does support compound indices), or can it at least be improved?
You can create a mixin that recursively groups/indexes your objects:
_.mixin({
compoundIndexBy: function(lst, iteratees, context) {
if (iteratees.length === 1)
return _.indexBy(lst, iteratees[0], context);
var grouped = _.groupBy(lst, iteratees[0], context);
_.each(grouped, function(sublst, k) {
grouped[k] = _.compoundIndexBy(sublst, _.rest(iteratees), context);
});
return grouped;
}
});
console.dir(_.compoundIndexBy(x, ['a', 'b', 'c']));
If you prefer a list of objects matching the given indexes (in case of non unique paths, for example):
_.mixin({
compoundGroupBy: function(lst, iteratees, context) {
var grouped = _.groupBy(lst, iteratees[0], context);
if (iteratees.length === 1)
return grouped;
_.each(grouped, function(sublst, k) {
grouped[k] = _.compoundGroupBy(sublst, _.rest(iteratees), context);
});
return grouped;
}
});
console.dir(_.compoundGroupBy(x, ['a', 'b', 'c']));
And a demo http://jsfiddle.net/nikoshr/8w4n31vb/