Let's say, I have an Array of object which looks like:
var jsonData = [
{"DS01":123,"DS02":88888,"DS03":1,"DS04":2,"DS05":3,"DS06":666},
{"DS01":123,"DS02":88888,"DS03":2,"DS04":3,"DS05":4,"DS06":666},
{"DS01":124,"DS02":99999,"DS03":3,"DS04":4,"DS05":5,"DS06":333},
{"DS01":124,"DS02":99999,"DS03":5,"DS04":6,"DS05":7,"DS06":333}
];
You can see there are some common key fields which are DS01, DS02 and DS06. Firstly, I want to find which are common group of keys.
For first 2 Objects : DS01 = 123, DS02 = 88888, DS06 = 666
For last 2 Objects : DS01 = 124, DS02 = 99999, DS06 = 333
I want to convert this array of objects to a format like this:
var jsonDataReduced =
[{
"DS01": 123,
"DS02": 88888,
"DS03": [1, 2],
"DS04": [2, 3],
"DS05": [3, 4],
"DS06": 666
},
{
"DS01": 124,
"DS02": 99999,
"DS03": [3, 5],
"DS04": [4, 6],
"DS05": [5, 7],
"DS06": 333
}
];
Let's say, I have another array of objects.
var jsonData2 = [{
"Mass": 3,
"Force": 3.1,
"Acceleration": 4
}, {
"Mass": 3,
"Force": 4.1,
"Acceleration": 4
}];
So after reducing it should be:
var jsonData2 = [{
"Mass": 3,
"Force": [3.1, 4.1],
"Acceleration": 4
}];
I have been trying to do these by using Array.reduce() but not getting an idea on how to do this job efficiently.
Is it possible to
making a single function
passing these kinds of array of objects as a parameter
and finally getting the reduced dataset
What I have tried :
var jsonData2 = [{
"Mass": 3,
"Force": 3.1,
"Acceleration": 4
}, {
"Mass": 3,
"Force": 4.1,
"Acceleration": 4
}];
const reduced = jsonData2.reduce((r, e, i, a) => {
if (i % 2 == 0) {
const next = a[i + 1];
const obj = { ...e, Force: [e.Force] }
if (next) obj.Force.push(next.Force);
r.push(obj)
}
return r;
}, []);
console.log(reduced);
You could get common keys and group by them.
var data = [{ DS01: 123, DS02: 88888, DS03: 1, DS04: 2, DS05: 3, DS06: 666 }, { DS01: 123, DS02: 88888, DS03: 2, DS04: 3, DS05: 4, DS06: 666 }, { DS01: 124, DS02: 99999, DS03: 3, DS04: 4, DS05: 5, DS06: 333 }, { DS01: 124, DS02: 99999, DS03: 5, DS04: 6, DS05: 7, DS06: 333 }],
common,
temp = data.reduce((r, o, i) => {
Object.entries(o).forEach(([k, v]) => {
r[k] = r[k] || [];
r[k][i] = v;
});
return r;
}, {}),
min = Infinity,
result;
Object.entries(temp).forEach(([k, a]) => {
var s = new Set;
temp[k] = a.map(v => s.add(v).size);
min = Math.min(min, s.size);
});
common = Object.keys(temp).filter(k => temp[k][temp[k].length - 1] === min);
result = data.reduce((r, o) => {
var temp = r.find(q => common.every(k => q[k] === o[k]));
if (!temp) {
r.push({ ...o });
} else {
Object.keys(o).filter(k => !common.includes(k)).forEach(k => Array.isArray(temp[k]) ? temp[k].push(o[k]) : (temp[k] = [temp[k], o[k]]));
}
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I am trying to figure out how to map a new array of objects that kind of creates teams by checking each array of users and, where there is a common users, moving that entire array into a new property that also features the notebookIds in common.
I have an array of objects structured like so:
const usersByNotebooks =
[
{
"notebookId": "abc",
"users": [1, 2, 3, 4]
},
{
"notebookId": "cde",
"users": [2, 3, 4]
},
{
"notebookId": "fgh",
"users": [3, 4, 5]
},
{
"notebookId": "qqq",
"users": [33, 16, 12]
},
]
So for the above data it would become something like this:
const teams =
[
{
"notebooksOnTeam": ["abc", "cde", "fgh"],
"usersOnTeam": [1, 2, 3, 4, 5]
},
{
"notebooksOnTeam": "qqq",
"usersOnTeam": [33, 16, 12]
},
]
I am using javascript and having trouble getting the logic down.
Loop over objects of array using reduce and check:
If the current notebook's users don't match any existing team with find method, so create a new team.
If the current notebook's users match an existing team, add the notebook to that team.
const usersByNotebooks = [{ "notebookId": "abc", "users": [1, 2, 3, 4] }, { "notebookId": "cde", "users": [2, 3, 4] }, { "notebookId": "fgh", "users": [3, 4, 5] }, { "notebookId": "qqq", "users": [33, 16, 12] }, ];
const teams = usersByNotebooks.reduce((result, current) => {
const teamFound = result.find((team) => team.usersOnTeam.some((user) => current.users.includes(user)));
if (!teamFound) {
result.push({
notebooksOnTeam: [current.notebookId],
usersOnTeam: current.users
});
} else {
teamFound.notebooksOnTeam.push(current.notebookId);
current.users.forEach((user) => {
if (!teamFound.usersOnTeam.includes(user)) {
teamFound.usersOnTeam.push(user);
}
});
}
return result;
}, []);
console.log(teams)
You could have a look to any objects of the result set and either get the first object of the same group and add all other found and finally add the actual value or later add a new object.
This approach works for unsorted and not connected items which gets later a joint.
const
addIfNotExist = (array, value) => array.includes(value) || array.push(value),
usersByNotebooks = [{ notebookId: "abc", users: [1, 2, 3, 4] }, { notebookId: "cde", users: [2, 3, 4] }, { notebookId: "fgh", users: [3, 4, 5] }, { notebookId: "qqq", users: [33, 16, 12] }, { notebookId: "xxx", users: [6, 7] }, { notebookId: "yyy", users: [5, 6] }],
result = usersByNotebooks.reduce(
(r, { notebookId, users }) => users.reduce((s, user) => {
const objects = [];
let first;
for (const o of s) {
if (!o.users.includes(user) && !o.notebooks.includes(notebookId)) {
objects.push(o);
continue;
}
if (!first) objects.push(first = o);
o.users.forEach(addIfNotExist.bind(null, first.users));
o.notebooks.forEach(addIfNotExist.bind(null, first.notebooks));
}
if (first) {
addIfNotExist(first.users, user);
addIfNotExist(first.notebooks, notebookId);
} else {
objects.push({ users: [user], notebooks: [notebookId] });
}
return objects;
}, r),
[]
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is an abstract solution for any length of groups which are connected.
It works in three step:
Generate an array of pairs or more or less items in a tupel,
group connected items together in an aray of arrays and
map the items in the wanted format.
const
addIfNotExist = (array, value) => array.includes(value) || array.push(value),
groupConnectedParts = (r, a) => {
const objects = [];
let first;
for (const b of r) {
if (!a.some((v, i) => b[i].includes(v))) {
objects.push(b);
continue;
}
if (!first) objects.push(first = b);
b.forEach((group, i) => group.forEach(addIfNotExist.bind(null, first[i])));
}
if (first) a.forEach((v, i) => addIfNotExist(first[i], v));
else objects.push(a.map(v => [v]));
return objects;
},
usersByNotebooks = [{ notebookId: "abc", users: [1, 2, 3, 4] }, { notebookId: "cde", users: [2, 3, 4] }, { notebookId: "fgh", users: [3, 4, 5] }, { notebookId: "qqq", users: [33, 16, 12] }, { notebookId: "xxx", users: [6, 7] }, { notebookId: "yyy", users: [5, 6] }],
result = usersByNotebooks
.flatMap(({ notebookId, users }) => users.map(user => [notebookId, user]))
.reduce(groupConnectedParts, [])
.map(([notebooks, users]) => ({ notebooks, users }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I need to take sum of all the person's ages and heights.
Input Array:
[
{
"personId": 1,
"ages": [
1,
4,
5
],
"heights": [
1,
1,
2
]
},
{
"personId": 2,
"ages": [
4,
2,
2
],
"heights": [
1,
4,
2
]
},
{
"personId": 2,
"ages": [
2,
1,
1
],
"heights": [
12
]
}
]
Desired Output:
[
{
"type": "ages",
"values": 22
},
{
"type": "heights",
"values": 23
}
]
My Solution (Which is working perfectly fine):
var agesTotal = 0;
var heightsTotal = 0;
var resultArr = [];
var sourceArr = [{personId: 1, ages: [1,4,5], heights: [1,1,2]}, {personId: 2, ages: [4,2,2], heights: [1,4,2]}, {personId: 2, ages: [2,1,1], heights: [12]}];
for(var i = 0; i < sourceArr.length; i++){
if(sourceArr[i].ages){
if(i == 0){
resultArr.push({
type: "ages",
values: agesTotal
});
}
for(var n = 0; n < resultArr.length; n++){
if(resultArr[n].type == "ages"){
resultArr[n].values += agesTotal + sourceArr[i].ages.reduce((partialSum, a) => parseFloat(partialSum) + parseFloat(a), 0)
}
}
}
if(sourceArr[i].heights){
if(i == 0){
resultArr.push({
type: "heights",
values: heightsTotal
});
}
for(var n = 0; n < resultArr.length; n++){
if(resultArr[n].type == "heights"){
resultArr[n].values += heightsTotal + sourceArr[i].heights.reduce((partialSum, a) => parseFloat(partialSum) + parseFloat(a), 0)
}
}
}
}
This above code of mine is producing the correct response, but it seems like so much processing and unoptimized.
What I need is the best & optimized possible solution for this operation
Without a clearly defined performance objective, it's hard to provide an answer that I feel is satisfying. However, I think this is readable, concise, and not wasteful:
function sumArrays (accumulator, obj) {
for (const [key, value] of Object.entries(obj)) {
if (!Array.isArray(value)) continue;
accumulator[key] ??= 0;
for (const n of value) accumulator[key] += n;
}
}
function getSums (array) {
const accumulator = {};
for (const obj of array) sumArrays(accumulator, obj);
return Object.entries(accumulator).map(([type, values]) => ({type, values}));
}
// Or, instead, optimized only for the input keys:
function getSumsOptimized (array, keysToSum) {
const result = keysToSum.map(type => ({type, values: 0}));
for (const obj of array) {
for (const [index, key] of keysToSum.entries()) {
for (const n of obj[key]) result[index].values += n;
}
}
return result;
}
const input = [
{ personId: 1, ages: [1, 4, 5], heights: [1, 1, 2]},
{ personId: 2, ages: [4, 2, 2], heights: [1, 4, 2]},
{ personId: 2, ages: [2, 1, 1], heights: [12 ]},
];
console.log(getSums(input));
console.log(getSumsOptimized(input, ['ages', 'heights']));
const array = [
{
"personId": 1,
"ages": [
1,
4,
5
],
"heights": [
1,
1,
2
]
},
{
"personId": 2,
"ages": [
4,
2,
2
],
"heights": [
1,
4,
2
]
},
{
"personId": 2,
"ages": [
2,
1,
1
],
"heights": [
12
]
}
]
let heightsSum = 0;
let agesSum = 0;
array.forEach(element => {
element.ages.forEach(age => {
agesSum += age;
})
element.heights.forEach(height => {
heightsSum += height;
})
})
const arr = [
{
type: 'ages',
values: agesSum
},
{
type: 'heights',
values: heightsSum
}
]
console.log(arr)
I understand that for count an array in Angular with rxjs I use reduce, for example if I want count : [{id: 1, items: [10, 20, 30]}] I use the following code:
const item = from([{id: 1, items: [10, 20, 30]}]);
item.pipe(
map(actions => actions.items),
mergeAll(),
reduce((acc, i) => acc + i, 0)
).subscribe(p => console.log('Print 60: ', p));
The questions is How get make a reducer in the following array:
const items = [
{
id: 1,
boxing: [1, 2, 2]
},
{
id: 2,
boxing: [10, 10, 20]
}];
result expected:
[{
id: 1,
boxing: 5
},
{
id: 2,
boxing: 40
}]
I will appreciate your help
You can easily do this with javascript by using reduce and map as
const items = [
{id: 1, boxing: [1, 2, 2]},
{id: 2, boxing: [10, 10, 20]}
];
let reducedItems = items.map(val => {
return {
id: val.id,
boxing: val.boxing.reduce((a, i) => a + i)
}
});
console.log(reducedItems);
One approach can be this.
from(items).pipe(
mergeMap((data) => from(data['boxing']).pipe(
reduce((acc, x) => acc + x),
map((val) => { data['boxing'] = val; return data })
)),
toArray()
).subscribe((val) => console.log(val));
Having a matrix which contains many sub-arrays. Each array has the same length and each first element of them is a string followed by numeric elements having this form:
myArray = [
["revenues", 10, 20, 30],
["expenses", 1, 1, 1],
["expenses", 2, 3, 4],
["revenues", 5, 6, 7],
];
My goal is to combine them by string and compute the sum on each position. For the above example the result must be:
result = [
["revenues", 15, 26, 37],
["expenses", 3, 4, 5],
];
I tried to do it by mapping them by the value of the string and than compute sum for each position. It's only for the sub-arrays strings containing "revenues" in the first phase but still not working.
result = myArray.map(s => s[0].includes("revenues")).reduce(function (r, a) {
a.forEach(function (b, i) {
r[i] = (r[i] || 0) + b;
});
return r;
}, []);
Any suggestions?
You could find the sub array in the temporary result or add that array to the result set.
var array = [["revenues", 10, 20, 30], ["expenses", 1, 1, 1], ["expenses", 2, 3, 4], ["revenues", 5, 6, 7]],
result = array.reduce((r, a) => {
var sub = r.find(([key]) => key === a[0]);
if (!sub) {
return r.concat([a]);
}
a.forEach((v, i) => i && (sub[i] += v));
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var myArray = [
["revenues", 10, 20, 30],
["expenses", 1, 1, 1],
["expenses", 2, 3, 4],
["revenues", 5, 6, 7],
];
var result = myArray.reduce(function(acc, curr) {
if (acc[curr[0]]) {
acc[curr[0]] = acc[curr[0]].map(function(val, index) {
if (index) {
return val + curr[index];
}
return val
})
} else {
acc[curr[0]] = curr;
}
return acc
}, {})
console.log(Object.values(result))
I am using loadash in my project
i have object in array like this
[{
"rating": {
"time_to_table": 5,
"value_for_money": 5,
"innovation": 5,
"plating_decoration": 5,
"taste": 5
}
}, {
"rating": {
"time_to_table": 0,
"value_for_money": 3,
"innovation": 2,
"plating_decoration": 2,
"taste": 4
}
}]
Now I want output like this by adding each property and want single object like this
"rating": {
"time_to_table": 5,
"value_for_money": 8,
"innovation": 7,
"plating_decoration": 7,
"taste": 9
}
I have tried this
var obj = {
"time_to_table": 0,
"value_for_money": 0,
"innovation": 0,
"plating_decoration": 0,
"taste": 0,
"total" : 5 * data.length
}
_.forEach(data,function(d){
obj.time_to_table += d.rating.time_to_table;
obj.value_for_money += d.rating.value_for_money;
obj.innovation += d.rating.innovation;
obj.plating_decoration += d.rating.plating_decoration;
obj.taste += d.rating.taste;
});
With Lodash you can do this using reduce() and mergeWith() that takes custom function as parameter.
var data = [{"rating":{"time_to_table":5,"value_for_money":5,"innovation":5,"plating_decoration":5,"taste":5}},{"rating":{"time_to_table":0,"value_for_money":3,"innovation":2,"plating_decoration":2,"taste":4}}]
var result = _.reduce(data, function(r, e, i) {
if(i != 0) _.mergeWith(r.rating, e.rating, (a, b) => a + b)
return r;
}, data[0])
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
You can use reduce, and having as many property as you want in your objects.
The following function doesn't require to know anything about the kind of object you are passing, because there are no hardcoded properties
const arr = [{
"rating": {
"time_to_table": 5,
"value_for_money": 5,
"innovation": 5,
"plating_decoration": 5,
"taste": 5
},
a: {b: 1, c: 2}
}, {
"rating": {
"time_to_table": 0,
"value_for_money": 3,
"innovation": 2,
"plating_decoration": 2,
"taste": 4
},
a: {b: 3, c: 0}
}];
const result = arr.reduce(fn, {})
function fn (acc, val) {
for (let x in val) {
acc[x] = acc[x] || {};
for (let y in val[x]) {
acc[x][y] = acc[x][y] || 0;
acc[x][y] += val[x][y];
}
}
return acc;
}
console.log(result)
Array#reduce solution.
var arr = [{"rating":{"time_to_table":5,"value_for_money":5,"innovation":5,"plating_decoration":5,"taste":5}},{"rating":{"time_to_table":0,"value_for_money":3,"innovation":2,"plating_decoration":2,"taste":4}}], obj = arr[0]; arr.splice(0,1);
var res = arr.reduce(function(s, a) {
Object.keys(a.rating).forEach(function(v) {
s.rating[v] += a.rating[v];
})
return s;
}, obj);
console.log(res);
You can do it using Array#reduce() and Object.keys()
const data = [{rating:{time_to_table:5,value_for_money:5,innovation:5,plating_decoration:5,taste:5}},{rating:{time_to_table:0,value_for_money:3,innovation:2,plating_decoration:2,taste:4}}];
let finalObject = data.reduce((o, c) => {
Object.keys(c.rating).forEach(k => {
o.rating[k] = o.rating[k] || 0;
o.rating[k] += c.rating[k];
});
return o;
}, { rating: {} })
console.log(finalObject);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use a recursive approach for arbitrary nested object, to collect and sum values in the objects.
var data = [{ rating: { time_to_table: 5, value_for_money: 5, innovation: 5, plating_decoration: 5, taste: 5 } }, { rating: { time_to_table: 0, value_for_money: 3, innovation: 2, plating_decoration: 2, taste: 4 } }],
result = data.reduce(function iter(target, source) {
Object.keys(source).forEach(function (k) {
if (source[k] && typeof source[k] === 'object') {
iter(target[k] = target[k] || {}, source[k]);
return;
}
target[k] = (target[k] || 0) + +source[k];
});
return target;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }