How to merge two objects with duplicated key sumup value - javascript

I have two objects as follows:
const a = {
'2021-1': 10,
'2021-2': 8
}
const b = {
'2021-1': 10,
'2020-3': 10,
'2020-4': 15,
'2020-5': 12,
'2020-6': 4
}
I would like to merge two objects and sum up values for duplicated keys.
Expected result is:
{
'2021-1': 20,
'2021-2': 8,
'2020-3': 10,
'2020-4': 15,
'2020-5': 12,
'2020-6': 4
}

You can perform a reduce operation over the entries of the second object to sum the values of each key, using a copy of the first object as the initial value.
const a = {
'2021-1': 10,
'2021-2': 8
}
const b = {
'2021-1': 10,
'2020-3': 10,
'2020-4': 15,
'2020-5': 12,
'2020-6': 4
}
const res = Object.entries(b).reduce((acc,[k,v])=>{
acc[k] = (acc[k] || 0) + v;
return acc;
}, {...a});
console.log(res);

This utility function merges the two objects and sums conflicting keys, including all the values unique to either a or b.
const mergeWithSum = (a, b) =>
[...Object.keys(a), ...Object.keys(b)].reduce((combined, key) => {
combined[key] = (a[key] ?? 0) + (b[key] ?? 0);
return combined;
}, {});
const a = {
'2021-1': 10,
'2021-2': 8
}
const b = {
'2021-1': 10,
'2020-3': 10,
'2020-4': 15,
'2020-5': 12,
'2020-6': 4
}
console.log(mergeWithSum(a, b));

here is another solution
function mergeObj(obja, objb) {
const merged = {...obja};
for ([key, value] of Object.entries(objb)) {
if(merged.hasOwnProperty(key)) {
merged[key] = merged[key] + value
} else {
merged[key] = value
}
}
return merged
}
it uses a simple for of loop, from object.entries and we destructure the array that contains key and value

Related

Calculate average of an array with objects that has a nested Object

I am trying to loop through array of Objects and calculate the average of a nested Object containing several different keys.
This is the start array:
[{
course: "math",
id: 4,
values: {
2017: 8,
2018: 9
}
}, {
course: "math",
id: 4,
values: {
2017: 5,
2019: 7
}
}]
This is my goal:
{2017:6.5,2018:9,2019:7}
Now it returns correct for 2017 but NaN for 2018 and 2019. If anyone have better way of solving this that doesn't require so much please provide to.
This is what I have tried so far. I have been searching a lot but not really found anything I can use.
const testObject = [{
id: 4,
course: "math",
values: {
2017: 8,
2018: 9
}
},
{
id: 5,
course: "English",
values: {
2017: 8,
2018: 9
}
},
{
id: 4,
course: "math",
values: {
2017: 5,
2019: 7
}
},
{
id: 4,
course: "english",
values: {
2017: 5,
2019: 7
}
},
]
//First I filter out the id 4 and course Math
const mathid1 = testObject.filter((e) => e.id === 4 && e.course === "math");
//I than find all the different years
const ArrayOfAllYears = []
mathid1.map((element) => {
ArrayOfAllYears.push(Object.keys(element.values));
})
//I here find all the different years
const withDuplicates = ArrayOfAllYears.reduce(function(arrayOne, arrayTwo) {
return arrayOne.concat(arrayTwo);
}, []);
const withoutDuplicates = Array.from(new Set(withDuplicates));
//Here I just create the calculate average function
const Result = {}
const calculateAverage = (array) => {
const sum = array.reduce((a, b) => a + b);
return sum / array.length;
};
const newObj = {}
withoutDuplicates.map((year) => {
let reformattedArray = mathid1.map(obj => {
if (obj["values"][year]) {
return obj["values"][year]
}
})
newObj[year] = calculateAverage(reformattedArray)
})
console.log(newObj)
// I want to calculate the average of the mathid1 values and return it on a Object like {2017:..,2018..}
There are two simple steps to the problem.
First, you need to reduce the array to an object with years and values:
// this outputs
// { 2017: [8, 5], 2018: [9], 2019: [7] }
function byYear(array) {
// take each item of an array
return array.reduce((acc, data) => {
// take the values of that item
Object.entries(data.values).forEach(([year, value]) => {
// and map all the values to years
acc[year] = acc[year] || []
acc[year].push(value)
})
return acc
}, {})
}
The second step is just taking averages:
function average(object) {
const averages = {}
for (let key in object) {
averages[key] = object[key].reduce((sum, value) => sum + value) / object[key].length
}
return averages
}
And now you put them together:
average(byYear(input))
In here, the input is the filtered array. As a whole snippet:
function byYear(array) {
return array.reduce((acc, data) => {
Object.entries(data.values).forEach(([year, value]) => {
acc[year] = acc[year] || []
acc[year].push(value)
})
return acc
}, {})
}
function average(object) {
const averages = {}
for (let key in object) {
averages[key] = object[key].reduce((sum, value) => sum + value) / object[key].length
}
return averages
}
const output = average(byYear([{
course: "math",
id: 4,
values: {
2017: 8,
2018: 9
}
}, {
course: "math",
id: 4,
values: {
2017: 5,
2019: 7
}
}]))
console.log(output)
The problem with your current code lies in how you build the reformattedArray variable. First, notice that your map function implicitly returns undefined whenever that year is missing from the current object:
let reformattedArray = mathid1.map(obj => {
if (obj["values"][year]) {
return obj["values"][year]
}
// There is an implicit return undefined, right here...
})
When you use the array .map method, every item of the array will be replaced by the return value of the map function. In the case that the year is not present, it will not go into the if block, and so it implicitly returns undefined upon reaching the end of the function.
So, ultimately all you have to do is remove the undefined entries from this array, and your code will work as-is.
One way to do that is to just use .filter(Boolean) on the array, which removes any falsey entries (which undefined is). Eg:
let reformattedArray = mathid1.map(obj => {
/* code here */
}).filter(Boolean); // Note the filter here...
Here is your snippet with that modification:
const testObject = [{
id: 4,
course: "math",
values: {
2017: 8,
2018: 9
}
},
{
id: 5,
course: "English",
values: {
2017: 8,
2018: 9
}
},
{
id: 4,
course: "math",
values: {
2017: 5,
2019: 7
}
},
{
id: 4,
course: "english",
values: {
2017: 5,
2019: 7
}
},
]
//First I filter out the id 4 and course Math
const mathid1 = testObject.filter((e) => e.id === 4 && e.course === "math");
//I than find all the different years
const ArrayOfAllYears = []
mathid1.map((element) => {
ArrayOfAllYears.push(Object.keys(element.values));
})
//I here find all the different years
const withDuplicates = ArrayOfAllYears.reduce(function(arrayOne, arrayTwo) {
return arrayOne.concat(arrayTwo);
}, []);
const withoutDuplicates = Array.from(new Set(withDuplicates));
//Here I just create the calculate average function
const Result = {}
const calculateAverage = (array) => {
const sum = array.reduce((a, b) => a + b);
return sum / array.length;
};
const newObj = {}
withoutDuplicates.map((year) => {
let reformattedArray = mathid1.map(obj => {
if (obj["values"][year]) {
return obj["values"][year]
}
}).filter(Boolean)
newObj[year] = calculateAverage(reformattedArray)
})
console.log(newObj)
// I want to calculate the average of the mathid1 values and return it on a Object like {2017:..,2018..}
Group items by year.
Calculate average.
const items=[{
course: "math",
id: 4,
values: {
2017: 8,
2018: 9
}
}, {
course: "math",
id: 4,
values: {
2017: 5,
2019: 7
}
}]
const groupedValues=items.reduce((groupedValues,item)=>{
Object.entries(item.values).forEach(([year,value])=>{
if(groupedValues[year]){
groupedValues[year]={value:groupedValues[year].value+value,items:groupedValues[year].items+1};
} else {
groupedValues[year]={value,items:1};
}
});
return groupedValues;
},{})
console.log(groupedValues);
const result = Object.entries(groupedValues).reduce((result,item)=>{
result[item[0]]=item[1].value/item[1].items;
return result;
},{})
console.log(result);
I would recommend extracting the years information into a map:
/** #type {Map<string, number[]} */
const years = new Map();
testObject.forEach((obj) => {
Object.keys(obj.values).forEach((key) => {
if (!years.has(key)) years.set(key, []);
years.set(key, [...years.get(key), obj.values[key]]);
});
});
Then you can simply loop over the map and create the resulting object:
const result = {};
years.forEach((values, key) => {
Object.defineProperty(result, key, {
value: values.reduce((acc, val) => acc + val) / values.length,
enumerable: true,
});
});
console.log(result);
It should output:
{ '2017': 6.5, '2018': 9, '2019': 7 }

React - Sort object entries by key in a specific order

I have an object
{
"undefined": 10,
"women": 5,
"men": 3,
}
And I need to sort it, so it looks like
{
"men": 3,
"women": 5,
"undefined": 10,
}
How can I do it?
I have tried to sort it like that, but it works alphabetically.
const object = {
"undefined": 10,
"women": 3,
"men": 6
}
const entries = Object.entries(object).sort();
const sortedObject = Object.fromEntries(entries);
console.log(sortedObject);
/*
Expected result:
{
"men": 6,
"women": 3,
"undefined": 10
}
*/
This is what I am trying to do:
statsParser.js
import { t } from "i18n";
export default (genders) => {
if (!genders) return;
const entries = Object.entries(genders).sort(([, a], [, b]) => b - a); // What if I want to order the map so it looks like { men: x, women: y, undefined: z } instead of by value in desc order?
const translatedEntries = entries.map((entry) => [
t(`content.stats.genders.${entry[0].toLowerCase()}`),
entry[1],
]);
const translatedGenders = Object.fromEntries(translatedEntries);
return translatedGenders;
};
To sort json object using key is easy as below:
const unordered = {
"undefined": 10,
"women": 5,
"men": 3,
}
const ordered = Object.keys(unordered).sort().reduce(
(obj, key) => {
obj[key] = unordered[key];
return obj;
},
{}
);
console.log(JSON.stringify(ordered));
To sort json object using value, you need convert your object into array using Object.entries. Then you sort that array using value and prepare string using map function and finally parse it into json.
const unordered = {
"undefined": 10,
"women": 5,
"men": 3,
}
const arrOfArrays = Object.entries(unordered);
const ordered = arrOfArrays.sort((a, b) => {
const aVal = Object.values(a)[1];
const bVal = Object.values(b)[1];
return aVal - bVal;
});
var jsonString = "{";
ordered.map(obj => {
var json = { ...obj };
jsonString += `"${json[0]}":"${json[1]}",`;
});
jsonString = jsonString.replace(/,\s*$/, "");
jsonString += "}";
console.log(JSON.parse(jsonString));

How do I create new object after manipulation

I have a JavaScript object like the following below availability and reserved, here I need to subtract quantity value from availability.
var availability = {"bike":10,"cycle":3,"car":1};
var reserved ={"cycle":1,"bike":10}
how should I get this response as below?
response = {"bike":0,"cycle":2,"car":1};
Why not a simple for loop.
var availability = { bike: 10, cycle: 3, car: 1 };
var reserved = { cycle: 1, bike: 10 };
let response = {};
for (let key in availability) {
if (reserved[key]) {
response[key] = availability[key] - reserved[key];
} else {
response[key] = availability[key];
}
}
console.log(response);
Output:
{ bike: 0, cycle: 2, car: 1 }
There are many way to solve this, but I recommend using reduce().
var availibilty = {
"bike": 10,
"cycle": 3,
"car": 1
};
var reserved = {
"cycle": 1,
"bike": 10
}
function calc(a, b) {
const answer = Object.keys(a).reduce((acc, key) => {
return {
...acc,
[key]: a[key] - (b[key] || 0)
}
}, {});
console.log(answer);
}
calc(availibilty, reserved);
You can iterate through each key-value pair and subtract quantity in availability with the corresponding key in reserved. Then create your result object using Object.fromEntries().
const availability = { "bike" : 10, "cycle" : 3, "car" : 1 },
reserved ={ "cycle": 1,"bike": 10 },
result = Object.fromEntries(Object.entries(availability).map(([key, value]) => [key, value - (reserved[key] ?? 0)]));
console.log(result);
You can loop through the Object. keys() of one object you provided and subtract the other using reduce() method.
var availibilty = {"bike":10,"cycle":3,"car":1};
var reserved ={"cycle":1,"bike":10}
let response = Object.keys(availibilty).reduce((x, y) => {
x[k] = availibilty[y] - reserved[y];
return x;
}, {});
console.log(response);
Please find Array.reduce implementation.
Logic
Loop through keys of availability object.
Find the values of each keys from reserved object.
Store the difference as the value for same key in the accumulator array.
var availability = { "bike": 10, "cycle": 3, "car": 1 };
var reserved = { "cycle": 1, "bike": 10 };
const response = Object.keys(availability).reduce((acc, curr) => {
acc[curr] = availability[curr] - (reserved[curr] || 0);
return acc;
}, {});
console.log(response);

How to search a value in Object which has values in array as well?

I've an object as below;
FOO: {
BAR: [9,32,8,12 ],
ZET: 4,
BETA: [3,14,6,2],
ALPHA: 37
},
I need to search values of this object to match values with keys. There are tons of samples which gives keys with a singular value but I couldn't find any sample which also able to search in values those are in arrays.
How can I be able to search whole of those values of FOO object and match with keys?
Note: The aim; I'll looking for an input and if given input is 2 then expect to get the key BETA or if given input is 4 then ZET.
So basically the values will be some pre-defined unique values already.
You could get the keys, create an array of values and check with includes. Return the found key.
function find(object, value) {
return Object
.keys(object)
.find(k => [].concat(object[k]).includes(value));
}
var object = { FOO: { BAR: [9, 32, 8, 12], ZET: 4, BETA: [3, 14, 6, 2], ALPHA: 37 } };
console.log(find(object.FOO, 4));
console.log(find(object.FOO, 8));
You can do it with a simple for...in loop and .concAT() and .includes() methods of arrays:
let obj = {
BAR: [9,32,8,12 ],
ZET: 4,
BETA: [3,14,6,2],
ALPHA: 37
};
let locator = (o, v) => {
for (var prop in o) {
if([].concat(o[prop]).includes(v)){
return prop;
}
}
}
console.log(locator(obj, 2));
console.log(locator(obj, 4));
You can simply loop though the object and search for the value to identify the key. If multiple matches are found, an array of keys that matches the result will be returned.
function objSearch(key, obj) {
const keys = [];
for (let item in obj) {
if (obj.hasOwnProperty(item)) {
if (obj[item] === key || (Array.isArray(obj[item]) && obj[item].indexOf(key) > -1)) {
keys.push(item);
}
}
}
return keys;
}
const obj = {
FOO: {
BAR: [9, 32, 8, 12],
ZET: 4,
BETA: [3, 14, 6, 2],
ALPHA: 37
}
};
const res1 = objSearch(14, obj.FOO); // Exist
const res2 = objSearch(15, obj.FOO); // Does not exist
const res3 = objSearch(37, obj.FOO); // Exist
console.log(res1);
console.log(res2);
console.log(res3);
Try this snippet,
I am checking conditions for object values whether its array or value and applied condition as per that. I kept parent key dynamic too.
var abc = {
FOO: {
BAR: [9, 32, 8, 12],
ZET: 4,
BETA: [3, 14, 6, 2],
ALPHA: 37
},
DAB: {
DBAR: [9, 32, 8, 12],
DZET: 4,
DBETA: [3, 14, 6, 2],
DALPHA: 37
},
};
function retTitle(abc, parent, k) {
var title = '';
$.each(abc[parent], function(x, y) {
if ((Array.isArray(y) && y.indexOf(k) != -1) || (!Array.isArray(y) && y == k)) {
title = x;
return;
}
});
return title;
}
var title = retTitle(abc, 'DAB', 4);
console.log(title);
Something like this should work:
// What we're searching
FOO = {
BAR: [9,32,8,12 ],
ZET: 4,
BETA: [3,14,6,2],
ALPHA: 37
};
function findValue(findValue, obj) {
return Object.entries(FOO)
.filter(([key,value]) => value === findValue || Array.isArray(value) && value.includes(findValue))
.map(([key,value])=> key);
}
function testfindValue(value, obj) {
console.log("testfindValue: \nInput: " + value, "Result: ", findValue(value,obj));
}
testfindValue(4, FOO);
testfindValue(6, FOO);
testfindValue(32, FOO);
testfindValue(99, FOO);
You can use a simple for...in loop to iterate over keys. Then check if the value is a number and equals to what you look for. If it's not a number then it's an array - so you check that this array includes the number you are looking for.
var FOO = {
BAR: [9,32,8,12 ],
ZET: 4,
BETA: [3,14,6,2],
ALPHA: 37
};
function findKeyByValue(obj, val) {
for (var i in FOO) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof FOO[i] === 'number') {
if(FOO[i] === val) return i;
} else {
if(FOO[i].includes(val)) return i;
}
}
}
console.log(findKeyByValue(FOO, 4));

BASIC Javascript array function, issue is known but I cannot fathom a solution

In the below function I am attempting to get an output which resembles this:
[[1,1,1,1],[2,2,2], 4,5,10,[20,20], 391, 392,591].
I can see that the problem I have embedded is that I am always adding the temp array with a push to the functions return, as a result, all of the individual numbers apart from the last number in the for each function are being pushed into the target array with the array object also.
I feel as though I need a further conditonal check but for the life of me I am unable to come up with solution which works.
Any suggestions would be much appreciated.
const sortme = (unsortedArr)=> {
let tempArr = [];
let outputArr = [];
const reorderedArr = unsortedArr.sort((a,b) => a-b);
reorderedArr.forEach((number, i) => {
if ((i === 0) || (reorderedArr[i] === reorderedArr[i-1])) {
tempArr.push(number);
}
else {
outputArr.push(tempArr);
tempArr = [];
tempArr.push(number);
}
})
outputArr.push(tempArr[0]);
return outputArr;
}
const unsortedArr = [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20];
sortme(unsortedArr);
i would make a deduped copy and .map() it to transform the values into arrays containing values from the original ( sorted ) array that you get using a .forEach :
const unsortedArr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
const sortMe = (arr) => {
arr = arr.sort((a, b) => a - b);
// a short way to dedupe an array
// results in : 1, 2, 4, 5, 10, 20, 391, 392, 591
let dedupe = [...new Set(arr)];
let tmpArr;
return dedupe.map(e => {
tmpArr = []; // empty tmpArr on each iteration
// for each element of the deduped array, look for matching elements in the original one and push them in the tmpArr
arr.forEach(a => {
if (a === e)
tmpArr.push(e);
})
if(tmpArr.length === 1)
return tmpArr[0]; // in case you have [4] , just return the 4
else
return tmpArr; // in case you have [1,1,1,1]
// shorthand for the if/else above
// return tmpArr.length === 1 ? tmpArr[0] : tmpArr;
});
}
const result = sortMe(unsortedArr);
console.log(result);
This should work (using reduce):
const unsortedArr = [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20];
let lastValue = null;
var newArr = unsortedArr.sort((a,b) => a-b).reduce((acc, value) => {
if (acc.length == 0 || ((acc.length > 0 || !acc[acc.length-1].length) && lastValue !== value)) {
acc.push(value);
} else if (acc.length > 0 && lastValue === value) {
acc[acc.length-1] = (acc[acc.length-1].length ? acc[acc.length-1].concat([value]): [value, value]);
}
lastValue = value;
return acc;
}, []);
console.log(newArr);
And another approach, just for fun:
const unsortedArr = [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20];
var arr = unsortedArr.sort((a,b) => a-b).reduce((acc, value) => {
if (acc.length > 0 && acc[acc.length-1].includes(value)) {
acc[acc.length-1].push(value);
} else {
acc.push([value])
}
return acc;
}, []).map((v) => v.length > 1 ? v: v[0]);
console.log(arr);
I hope the below one is quite simple;
function findSame(pos, sortedArr){
for(let i =pos; i<sortedArr.length; i++){
if(sortedArr[i] !== sortedArr[pos]){
return i
}
}
}
function clubSameNumbers(unsortedArr){
let sortedArr = unsortedArr.sort((a,b)=>a-b)
//[ 1, 1, 1, 1, 2, 2, 2, 4, 5, 10, 20, 20, 391, 392, 591 ]
let result = []
for(let i = 0; i < sortedArr.length; i = end){
let start = i
var end = findSame(i, sortedArr)
let arr = sortedArr.slice(i, end)
arr.length > 1 ? result.push(arr) : result.push(...arr)
}
return result
}
console.log(clubSameNumbers([1,2,4,591,392,391,2,5,10,2,1,1,1,20,20]))
//[ [ 1, 1, 1, 1 ], [ 2, 2, 2 ], 4, 5, 10, [ 20, 20 ], 391, 392, 591 ]

Categories

Resources