Replace months in javascript array - javascript

in this.sources I have this kind of array data:
this.sources = [
{
cost: 50000.5,
ids: { 1: "11112", 2: "1112", 3: "121212" },
months: { 1: "54548.5000", 2: "45.0000", 3: "510.5000" },
name: "test1"
},
{
cost: 12469134.5,
ids: { 1: "19689", 2: "19690", 3: "19691" },
months: { 1: "12345678.5000", 2: "0.0000", 3: "50.5000" },
name: "test2"
}
];
What I need is to replace all data in months for all cases (just to change the 4 decimals to 2) and then return the array with the same structure as it is.
What I tried:
this.sources = this.fixDecimal(this.sources);
fixDecimal(source) {
for ( let elx in source ) {
let newsource = source[elx];
for ( let x = 0; x <= 11; x++ ) {
let result = newsource.months[x];
if (result) { if (result) { result.map(y => y.toFixed(2));}
}
}
return result;
}
However this is not working at all. Is there any simple solution just to fix the decimals in months and return the same array with changed data?

The reason your solution doesnt work is because you have your values as string and you can't use toFixed on a string. The parseFloat in the snippet below mitigates that:
for(let i=0; i<dataset.length;i++){
let months = {};
// Instead of looping through a hardcoded amount, loop though all the keys:
Object.keys(dataset[i].months).forEach(function(key) {
months[key] = parseFloat(dataset[i].months[key]).toFixed(2);
});
dataset[i].months = months;
}
This is assuming that you get your results in a variable dataset, you might want to tweak this a bit.

Since all of your values are strings, and have 4 positions after the decimal separator, you can just slice() off the last two characters:
const months = {
1: "54548.5000",
2: "45.0000",
3: "510.5000"
// ...
};
for (let m in months) {
months[m] = months[m].slice(0, -2);
}
console.log(months);

This code should do what you want, without modifying the original array. Slice will remove the last 2 characters of each month (you can even use substring here: v.substring(0, v.length - 2))
:
sources = [
{
cost: 50000.5,
ids: { 1: "11112", 2: "1112", 3: "121212" },
months: { 1: "54548.5000", 2: "45.0000", 3: "510.5000" },
name: "test1"
},
{
cost: 12469134.5,
ids: { 1: "19689", 2: "19690", 3: "19691" },
months: { 1: "12345678.5000", 2: "0.0000", 3: "50.5000" },
name: "test2"
}
];
console.log(sources.map(o => ({...o, months: Object.entries(o.months).reduce((a, [k,v]) => ({...a, [k]: v.slice(0, -2)}), {})})))
Or, you could modify the original array:
sources = [
{
cost: 50000.5,
ids: { 1: "11112", 2: "1112", 3: "121212" },
months: { 1: "54548.5000", 2: "45.0000", 3: "510.5000" },
name: "test1"
},
{
cost: 12469134.5,
ids: { 1: "19689", 2: "19690", 3: "19691" },
months: { 1: "12345678.5000", 2: "0.0000", 3: "50.5000" },
name: "test2"
}
];
sources.forEach((o, i) => sources[i].months = Object.entries(o.months).reduce((a, [k,v]) => ({...a, [k]: v.slice(0, -2)}), {}))
console.log(sources)

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 }

Javascript - How to sum the values in such an array?

I have such an array:
let array = {
[1]: {
name: 'test 1',
count: 5
},
[2]: {
name: 'test 2',
count: 3
}
}
How can I sum the values in the "count" column? Examples from simple arrays do not work. I currently have such a loop. Can it be done somehow better?
let sum = 0
Object.entries(array).forEach(([key, val]) => {
sum += val.count
});
Use reduce
let array = { 1: { name: "test 1", count: 5, }, 2: { name: "test 2", count: 3, }, };
total = Object.values(array).reduce((t, { count }) => t + count, 0); //t accumulator accumulates the value from previous calculation
console.log(total);
if you want to use a forEach loop like in your method use Object.values() instead because you only need values to calculate the sum of count
let array = {
1: { name: "test 1", count: 5 },
2: { name: "test 2", count: 3 },
};
let sum = 0;
Object.values(array).forEach(({ count }) => {
sum += count;
});
console.log(sum);
Building on top of the answer provided by #Sven.hig
Since you are calling the object "array" you might want to use an actual array instead.
Creating some functions to abstract away the complexity will help you understand your code better, when you come back to it in the future.
const add = (a, b) => a + b;
const sum = arr => arr.reduce(add, 0);
const data = [{
name: "test 1",
count: 5,
}, {
name: "test 2",
count: 3,
}
];
const total = sum(
data.map(d => d.count)
);
console.log(total);

How to calculate values differences in an array of objects without a for loop, in JavaScript?

Say I have an array of objects:
const peopleArray = [
{
'date': '3/1',
'people': 0
},
{
'date: '3/2',
'people': 5
},
{
'date: '3/3',
'people': 8
},
...
];
I'm trying to draw a chart showing daily difference between the number of people, so I need the following array of objects:
[{
'date': '3/1,
'diff': 0 // Shows 0 the first day.
},
{
'date': '3/2',
'diff': 5 // 5 people joined.
},
{
'date': '3/3',
'diff': 3 // 3 more people joined.
},
...
];
I know I can achieve this with Array.push() and a for loop:
const peopleArray = [{
date: '3/1',
people: 0
},
{
date: '3/2',
people: 5
},
{
date: '3/3',
people: 8
}
];
let diffArray = [];
for (i = 0; i < peopleArray.length; i++) {
if (i === 0) {
// Shows 0 the first day
diffArray.push({
date: peopleArray[i].date,
diff: 0
});
} else {
diffArray.push({
date: peopleArray[i].date,
diff: peopleArray[i].people - peopleArray[i - 1].people
});
}
}
console.log(diffArray);
But is there a cleaner way to do this with map(), reduce() or any of those array functions?
You could use map() and do something like:
const peopleArray = [
{
date: "3/1",
people: 0,
},
{
date: "3/2",
people: 5,
},
{
date: "3/3",
people: 8,
},
];
let diffArray = peopleArray.map((o, i, a) => ({
date: o.date,
diff: i > 0 ? o.people - a[i - 1].people : 0,
}));
console.log(diffArray);
The map() takes a callback function that accepts three arguments:
currentValue - The current element being processed in the array;
index - The index of the current element being processed in the array:
array - The array map was called upon.
So you can use those parameters to achieve the same thing you've done with your for loop.
The reduce() is not really what you're looking for in this case because you want more than a single output value.
You can use the existing object as well if you want and use the same for the graph and any other place if needed.
const peopleArray = [{
date: '3/1',
people: 0
},
{
date: '3/2',
people: 5
},
{
date: '3/3',
people: 8
}
];
var peopleArrayLength = peopleArray.length;
if(peopleArrayLength > 0){
var peopleObject = peopleArray[0];
peopleObject.diff = 0;
peopleArray[0] = peopleObject;
for (i = 1; i < peopleArrayLength; i++) {
peopleObject = peopleArray[i];
peopleObject.diff = peopleArray[i].people - peopleArray[i-1].people;
peopleArray[i] = peopleObject;
}
}
console.log(peopleArray);
Outputs:
[
{
"date": "3/1",
"people": 0,
"diff": 0
},
{
"date": "3/2",
"people": 5,
"diff": 5
},
{
"date": "3/3",
"people": 8,
"diff": 3
}
]
You can use inline If Statement as follows:
for (i = 0; i < peopleArray.length; i++) {
var df=i!=0?(peopleArray[i].people - peopleArray[i - 1].people):0;
diffArray.push({
date: peopleArray[i].date,
diff: df
});
}

How to sum a specific option in an array of objects?

I have this array:
[
{ id: "1", option { points: "1"} },
{ id: "2", option { points: "20"} },
{ id: "3", option { points: "4"} },
]
I'm trying to sum all the objectoption that have as property points, so I did:
var total_points = this.my_arr.reduce(function(a, b){
return a + b.option.points;
}, 0);
but this return a concatenation of the index of each id, something like: 012, that is of course wrong.
The expected output is: 25
You must convert your strings to numbers, for example:
var total_points = this.my_arr.reduce(function(a, b){
return a + parseInt(b.option.points);
}, 0);
Correct your object and cast the b as Number. In the object the values are strings so when + is used with string concatenations happens not addition. That's why the strings are converted to numbers and then added
var e=[
{ id: "1", option :{ points: "1"} },
{ id: "2", option :{ points: "20"} },
{ id: "3", option :{ points: "4"} },
]
var total_points = e.reduce(function(a, b){
return a + Number(b.option.points);
}, 0);
console.log(total_points);
In your object options is string when you use + operator b/w two strings it joins both of then. Use you convert string to number using parseInt() or Number()
var total_points = this.my_arr.reduce(function(a, b){
return a + parseInt(b.option.points);
}, 0);
You must convert the string to a number, you can use parseInt for that, or even simpler prepend a + before the string.
This version works with NaN, undefined or missing properties:
const data = [
{ id: "1", option: { points: "1" } },
{ id: "2", option: { points: "20" } },
{ id: "3", option: { points: "4" } },
{ id: "4", option: { points: NaN } },
{ id: "5", option: { points: undefined } },
{ id: "6", option: { } },
{ id: "7" },
];
const totalPoints = data.reduce((accum, elem) => {
const value = elem.option && elem.option.points ? +elem.option.points : 0;
return accum + value;
}, 0);
console.log(totalPoints);

How to compare keys of Objects in JavaScript?

I need your help:
I have an Array (data) containing objects:
var data = [
0: Object { hex: "#ff847f", length: "20" }
1: Object { hex: "#afff90", length: "18" }
2: Object { hex: "#afff90", length: "40" }
3: Object { hex: "#6d91b0", length: "30" }
4: Object { hex: "#ff847f", length: "20" }
]
I need a function, that results me an new Array, which has only unique hex-values AND add up the length-values of the equal hex-values.
The result should look like this:
var data2 = [
0: Object { hex: "#ff847f", length: "40" }
1: Object { hex: "#afff90", length: "58" }
2: Object { hex: "#6d91b0", length: "30" }
]
Thanks for ur ideas.
This is probably not the best solution, but it works. The challenging bit is the fact that you want your length as a string instead of a number. Here is my solution, I hope it helps!
const transformArray = (arr) => {
//first you need to convert the lengths to numbers to make adding easier
arr.map((item) => {
item.length = Number(item.length)
})
//then combine like objects
let output = [];
arr.forEach((dataObj) => {
let found=false;
for (let i=0; i<output.length; i++) {
if (output[i].hex === dataObj.hex) {
output[i].length+= dataObj.length;
found=true;
}
}
if (found===false) {
output.push(dataObj)
}
});
//then convert your lengths back to strings
arr.map((item) => {
item.length = item.length.toString();
})
return output;
}

Categories

Resources