is there a way to turn
[ { JavaScript: 41837, Batchfile: 47 },
{ 'C#': 7484 },
{ Batchfile: 110 },
{ Scala: 50597 },
{ Java: 18180 },
{ Java: 55689 } ]
into:
{
JavaScript: 41837,
Batchfile: 157,
'C#': 7484
Scala: 50597,
Java: 73869
}
The size of the array is different every time we run the application.
reduce is your friend
const list = [ { JavaScript: 41837, Batchfile: 47 },
{ 'C#': 7484 },
{ Batchfile: 110 },
{ Scala: 50597 },
{ Java: 18180 },
{ Java: 55689 } ];
const summed = list.reduce((totals, obj) => {
Object.keys(obj).forEach(k => {
const cur = totals[k] || 0;
totals[k] = cur + obj[k];
});
return totals;
}, {});
console.log(summed);
Make a new object. Iterate through each object in the input array. For each object, iterate through the keys of that object. If they key exists in your new object, add the value, otherwise add the key and value to the new object;
var arr = [ { JavaScript: 41837, Batchfile: 47 },
{ 'C#': 7484 },
{ Batchfile: 110 },
{ Scala: 50597 },
{ Java: 18180 },
{ Java: 55689 } ]
var result = {}
arr.forEach(function(group){
Object.keys(group).forEach(function(key){
if(result[key]) {
result[key] += group[key];
} else {
result[key] = group[key];
}
})
});
console.log(result);
Array reduce in combination with For in to loop through the object keys in them would get what you want.
a = [ { JavaScript: 41837, Batchfile: 47 },
{ 'C#': 7484 },
{ Batchfile: 110 },
{ Scala: 50597 },
{ Java: 18180 },
{ Java: 55689 } ]
result = {}
b = a.reduce((result, item) => {
for (let eachKey in item) {
const curSum = result[eachKey] || 0;;
result[eachKey] = curSum + item[eachKey];
}
return result;
}, result)
Related
Consider the following array of objects in javascript
const array = [
{ 10205: 2 },
{ 10207: 3 },
{ 10205: 2 },
{ 10207: 1 }
]
I would like to have it converted to
array = [
{ 10205: 4 },
{ 10207: 4 }
]
const array = [{ 10205: 2 }, { 10207: 3 }, { 10205: 2 }, { 10207: 1 }];
const newArray = [];
array.forEach((element) => {
const elementKey = Object.keys(element)[0];
const foundIndex = newArray.findIndex((_) => Object.keys(_)[0] === elementKey);
if (foundIndex >= 0) {
newArray[foundIndex] =
{[elementKey]: newArray[foundIndex][elementKey] + element[elementKey]};
} else newArray.push(element);
});
console.log(newArray)
Please use reduce function.
const array = [
{ 10205: 2 },
{ 10207: 3 },
{ 10205: 2 },
{ 10207: 1 }
]
console.log(Object.values(array.reduce((acc, el)=>{
Object.keys(el).map((key)=>{
acc[key] = {
[key]: (acc?.[key]?.[key] ?? 0) + el[key],
}
})
return acc;
}, {})));
I'm having an array look like this
[
{
object1:{ childObj1:[grandChild1,grandChild2], childObj1, childObj1}
},
{
object2:{ childObj1:[grandChild1,grandChild2], childObj1, childObj1}
},
{
object3:{ childObj1:[grandChild1,grandChild2], childObj1, childObj1}
},
]
Now i want to get rid of every nested item that have more than one item and keep only the first, like :
[
{
object1:{ childObj1:[grandChild1]}
},
{
object2:{ childObj2:[grandChild1]}
},
{
object3:{ childObj3:[grandChild1]}
},
]
Which is the most effective way to do this?
const arr = [{
object1: {
childObj1: ['grandChild1', 'grandChild2'],
childObj3: 't',
childObj2: 'r'
}
},
{
object2: {
childObj1: ['grandChild1', 'grandChild2'],
childObj3: 't',
childObj2: 'r'
}
},
{
object3: {
childObj1: ['grandChild1', 'grandChild2'],
childObj3: 't',
childObj2: 'r'
}
},
];
console.log(arr.map(el => {
const child = el[Object.keys(el)[0]];
const firstProperty = {
[Object.keys(child)[0]]: child[Object.keys(child)[0]]
};
return {
[Object.keys(el)[0]]: firstProperty
}
}));
Hope this is what you wanted.
Try this:
const arr = [{
object1: {
childObj1: ['grandChild1', 'grandChild2'],
childObj2: {},
childObj3: {}
}
},
{
object2: {
childObj1: ['grandChild1', 'grandChild2'],
childObj2: {},
childObj3: {}
}
},
{
object3: {
childObj1: ['grandChild1', 'grandChild2'],
childObj2: {},
childObj3: {}
}
},
]
function f(obj, startLevel, currentLevel = 0) {
let value
if (Array.isArray(obj)) {
value = []
} else if (typeof obj === 'object') {
value = {}
} else {
value = obj
}
if (typeof obj === 'object') {
for (let i in obj) {
if (currentLevel >= startLevel) {
if (Array.isArray(obj[i])) {
value[i] = obj[i].length ? [f(obj[i][0], startLevel, currentLevel + 1)] : [];
} else {
value[i] = f(obj[i], startLevel, currentLevel + 1)
}
break;
} else {
value[i] = f(obj[i], startLevel, currentLevel + 1)
}
}
}
return value;
}
console.log(f(arr, 1));
I need to sort nested array while sorting order and the sorting key will be dynamic. I am using the query but it work only on plain text.
Sample Data:
[
{
"modelId":1,
"modelCode":"model1",
"price":[
{
"PRICE_CODE1":225.01
},
{
"PRICE_CODE2":247.68
},
{
"PRICE_CODE3":298.0
}
]
},
{
"modelId":2,
"modelCode":"model2",
"price":[
{
"PRICE_CODE1":100.01
},
{
"PRICE_CODE2":200.68
},
{
"PRICE_CODE3":300.0
}
]
}
]
Expected Output:
[
{
"modelId":2,
"modelCode":"model2",
"price":[
{
"PRICE_CODE1":100.01
},
{
"PRICE_CODE2":200.68
},
{
"PRICE_CODE3":300.0
}
]
},
{
"modelId":1,
"modelCode":"model1",
"price":[
{
"PRICE_CODE1":225.01
},
{
"PRICE_CODE2":247.68
},
{
"PRICE_CODE3":298.0
}
]
}
]
as per the above example sorting is PRICE_CODE1, modelCode with ascending order. I am using the below query-
function sortByMultipleKey(keys) {
return function(a, b) {
if (keys.length == 0) return 0; // force to equal if keys run out
key = keys[0]; // take out the first key
if (a[key] < b[key]) return -1; // will be 1 if DESC
else if (a[key] > b[key]) return 1; // will be -1 if DESC
else return sortByMultipleKey(keys.slice(1))(a, b);
}
}
arr.sort(sortByMultipleKey(['PRICE_CODE1','modelCode']))
above query is working for plain arrays not for arrays of arrays because in example price is array. How to achieve this?
Warning: This would work for the price array structure you have but not for modelCode as sort key.
const data = [
{
"modelId":2,
"modelCode":"model2",
"price":[
{
"PRICE_CODE1":100.01
},
{
"PRICE_CODE2":200.68
},
{
"PRICE_CODE3":300.0
}
]
},
{
"modelId":1,
"modelCode":"model1",
"price":[
{
"PRICE_CODE1":225.01
},
{
"PRICE_CODE2":247.68
},
{
"PRICE_CODE3":298.0
}
]
}
]
function sortData(sortKeys = []) {
data.sort((a,b) => {
for (let sortKey of sortKeys) {
const priceObjA = a.price.find(x => sortKey in x);
const priceObjB = b.price.find(x => sortKey in x);
const priceA = priceObjA && priceObjA[sortKey] || 0;
const priceB = priceObjB && priceObjB[sortKey] || 0;
const result = priceA - priceB;
if (result !== 0) {
return result;
}
}
// fallback for equality
return 0;
})
}
sortData(['PRICE_CODE1']);
console.log(JSON.stringify(data,null,2));
sortData(['PRICE_CODE2']);
console.log(JSON.stringify(data,null,2));
sortData(['PRICE_CODE3']);
console.log(JSON.stringify(data,null,2));
arr.sort(sortByMultipleKey(['PRICE_CODE1','modelCode']))
Your sortByMultipleKey takes a list of keys, each of which describe one key. That can't describe the PRICE_CODE1 field of an object under prices.
You're essentially trying to come up with a syntax to describe arbitrary location in hierarchical data.
Instead of doing that, Use Javascript itself to define how to find the next comparison field! pass functions that can resolve the fields and iterate over those.
Below, I will define 2 functions. The first will extract the first PRICE_CODE1 from the elements of prices.
function(d) {
for (i = 0; i < d.price.length; d++) {
if ("PRICE_CODE1" in d.price[i]) {
return d.price[i].PRICE_CODE1
}
}
return undefined
}
The one for modelCode is simpler:
function(d) {
return d.modelCode
}
I also add a 3rd model with the same PRICE_CODE1 so that modelCode would also be relevant.
function sortByMultiple(field_funcs) {
return function(a, b) {
for (i = 0; i < field_funcs.length; i++) {
fa = field_funcs[i](a)
fb = field_funcs[i](b)
console.log("Comparing " + fa + " and " + fb)
if (fa < fb) return -1;
if (fa > fb) return 1;
if (i + 1 == field_funcs.length) return 0
}
}
}
var data = [{
"modelId": 2,
"modelCode": "model2",
"price": [{
"PRICE_CODE1": 100.01
},
{
"PRICE_CODE2": 200.68
},
{
"PRICE_CODE3": 300.0
}
]
},
{
"modelId": 1,
"modelCode": "model1",
"price": [{
"PRICE_CODE1": 225.01
},
{
"PRICE_CODE2": 247.68
},
{
"PRICE_CODE3": 298.0
}
]
},
{
"modelId": 3,
"modelCode": "model3",
"price": [{
"PRICE_CODE1": 225.01
},
{
"PRICE_CODE2": 247.68
},
{
"PRICE_CODE3": 298.0
}
]
}
]
data.sort(sortByMultiple([
function(d) {
for (i = 0; i < d.price.length; d++) {
if ("PRICE_CODE1" in d.price[i]) {
return d.price[i].PRICE_CODE1
}
}
return undefined
},
function(d) {
return d.modelCode
}
]))
console.log(data)
You can use lodash library:
_.orderBy( data, [c => c.price[0].PRICE_CODE1, "modelCode"], ["asc", "asc"]);
This is the array of objects I'm fetching (the source isn't mine so I can't change the structure of this data server-side in any way).
[
{ phase4: 3 },
{ phase3: 4 },
{ phase2: 14 },
{ phase1: 46 },
{ phase6: 12 },
{ phase5: 1 }
]
Is there a way I could dynamically sort the given array to the following?
[
{ phase1: 46 },
{ phase2: 14 },
{ phase3: 4 },
{ phase4: 3 },
{ phase5: 12 },
{ phase6: 1 },
]
UPDATE
jsfiddle
You can sort with the customised sort algorithm. You just need to grab the key using Object.keys(obj)[0]
const arr = [
{ phase4: 3 },
{ phase3: 4 },
{ phase2: 14 },
{ phase1: 46 },
{ phase6: 12 },
{ phase5: 1 },
];
const result = arr.sort((a, b) => {
const prop1 = Object.keys(a)[0];
const prop2 = Object.keys(b)[0];
if (prop1 < prop2) return -1;
if (prop1 > prop2) return 1;
return 0;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could just take the number from the key in each object and use a numeric sort()
const d=[
{ phase4: 3 },
{ phase3: 4 },
{ phase2: 14 },
{ phase1: 46 },
{ phase6: 12 },
{ phase5: 1 },
{ phase11: 11 },
{ phase22: 22 }
]
const keyNum = (e) => Object.keys(e)[0].match(/\d+$/)[0];
const res = d.sort((a,b) => keyNum(a) - keyNum(b))
console.log(res)
Sorry for the title, I'm not even sure how to phrase what is happening here.
I'm working on an expense tracking program in React that supports multiple currencies. The expenses being tracked can be nested arbitrarily deep in a JSON object.
entertainment: {
_values: {
USD: 23,
AUD: 5,
},
'food & drink': {
_values: {
AUD: 83,
},
'local bar': {
_values: {
AUD: 28,
USD: 2,
},
},
},
minigolf: {
_values: {
USD: 112,
},
}
}
An expense can have an amount stored directly in it, but it can also act as a 'parent' category to further detailed sub-expenses.
To display the total value of an expense I've written a pair of functions:
sumValues(values)
Sums up an array of _values objects (a value object is a key-value store of currency codes and integers)
totalExpense(expense)
Returns the total value of an expense. ie any _values it has, + the totalExpense of any children expenses.
I thought I'd written these as pure functions, but when calling totalExpense() recursively the first child of an expense returns the wrong total.
totalExpense(entertainment);
//-> Object { USD: 137, AUD: 116 }
OK
totalExpense(entertainment['food & drink']);
//-> Object { AUD: 111, USD: 2 }
OK
totalExpense(entertainment);
totalExpense(entertainment['food & drink']);
//-> Object { AUD: 139, USD: 4 }
NOT OK
I've been poking at this code for hours now, but for the life of me can't see what is happening:
sumValues = values => {
return values.reduce((acc, cur) => {
for (const currency in cur) {
acc[currency]
? (acc[currency] = acc[currency] + cur[currency])
: (acc[currency] = cur[currency]);
}
return acc;
});
};
totalExpense = expense => {
const values = [];
if (expense['_values']) {
values.push(expense['_values']);
}
const subExpenses = Object.keys(expense).filter(child => {
return child[0] !== '_';
});
if (subExpenses.length > 0) {
for (const subExpense of subExpenses) {
let subtotal = this.totalExpense(expense[subExpense]);
values.push(subtotal);
}
}
if (values.length) {
return this.sumValues(values);
} else {
throw Error('No values in this expense');
}
};
render() {
const entertainment = {
_values: {
USD: 23,
AUD: 5,
},
'food & drink': {
_values: {
AUD: 83,
},
'local bar': {
_values: {
AUD: 28,
USD: 2,
},
},
},
minigolf: {
_values: {
USD: 112,
},
},
};
console.log(this.totalExpense(entertainment));
console.log(this.totalExpense(entertainment['food & drink']));
console.log(this.totalExpense(entertainment['minigolf']));
return;
}
The problem is that your reduce callback's initial value is the first item in the values array, and then you proceed to assign to that item:
acc[currency]
? (acc[currency] = acc[currency] + cur[currency])
: (acc[currency] = cur[currency]);
So, the first item gets mutated every time sumValues is called. Instead, provide an empty object as the initial value for the reduce:
sumValues = values => {
return values.reduce((acc, cur) => {
for (const currency in cur) {
acc[currency]
? (acc[currency] = acc[currency] + cur[currency])
: (acc[currency] = cur[currency]);
}
return acc;
}, {});
};
sumValues = values => {
return values.reduce((acc, cur) => {
for (const currency in cur) {
acc[currency] ?
(acc[currency] = acc[currency] + cur[currency]) :
(acc[currency] = cur[currency]);
}
return acc;
}, {});
};
totalExpense = expense => {
const values = [];
if (expense['_values']) {
values.push(expense['_values']);
}
const subExpenses = Object.keys(expense).filter(child => {
return child[0] !== '_';
});
if (subExpenses.length > 0) {
for (const subExpense of subExpenses) {
let subtotal = this.totalExpense(expense[subExpense]);
values.push(subtotal);
}
}
if (values.length) {
return this.sumValues(values);
} else {
throw Error('No values in this expense');
}
};
const entertainment = {
_values: {
USD: 23,
AUD: 5,
},
'food & drink': {
_values: {
AUD: 83,
},
'local bar': {
_values: {
AUD: 28,
USD: 2,
},
},
},
minigolf: {
_values: {
USD: 112,
},
},
};
console.log(totalExpense(entertainment));
console.log(totalExpense(entertainment['food & drink']));