3

I have the following arrays:

const countries = ['Belgium', 'Uk']
const years = ['2019', '2018', '2017']
const colors = ['red', 'orange', 'green']

I want an array like that:

const result = [
 {
 country: 'Belgium',
 year: '2019',
 red: random(min, max),
 orange: random(min, max),
 green: random(min, max),
 },
 {
 country: 'Belgium',
 year: '2018',
 red: random(min, max),
 orange: random(min, max),
 green: random(min, max),
 },
 {
 country: 'Belgium',
 year: '2017',
 red: random(min, max),
 orange: random(min, max),
 green: random(min, max),
 },
 {
 country: 'Uk',
 year: '2019',
 red: random(min, max),
 orange: random(min, max),
 green: random(min, max),
 },
 {
 country: 'Uk',
 year: '2018',
 red: random(min, max),
 orange: random(min, max),
 green: random(min, max),
 },
 {
 country: 'Uk',
 year: '2017',
 red: random(min, max),
 orange: random(min, max),
 green: random(min, max),
 },
 {
 country: 'Tot',
 year: '2019',
 red: // sum of the values of the red key for each country in the year 2019,
 orange: // sum of the values of the orange key for each country in the year 2019,
 green: // sum of the values of the green key for each country in the year 2019,
 },
 {
 country: 'Tot',
 year: '2018',
 red: // sum of the values of the red key for each country in the year 2018,
 orange: // sum of the values of the orange key for each country in the year 2018,
 green: // sum of the values of the green key for each country in the year 2018,
 },
 {
 country: 'Tot',
 year: '2017',
 red: // sum of the values of the red key for each country in the year 2017,
 orange: // sum of the values of the orange key for each country in the year 2017,
 green: // sum of the values of the green key for each country in the year 2017,
 },
]

So, for each year, and for each country, there must be an object containing the keys of each color. The value must be random.

Then, should be other objects with country = Total and with values of colors key the sum of the values of other objects.

This is what I'm trying to do:

function createValues() {
 const min = 0
 const max = 300
 const dataset: any = []
 countries.forEach(country => {years.forEach(year => {colors.forEach(color => {dataset.push({country: country, year: year, [color]: random(min, max),})})})})}

But it doesn't work and I don't know how to compute the sum values.

double-beep
5,68019 gold badges43 silver badges50 bronze badges
asked Jan 31, 2019 at 10:10
1
  • I recommend making a seperate color object inside each country which contains the seperate colors. Commented Jan 31, 2019 at 13:19

7 Answers 7

3

You could get a cartesian result of countries and years, get all totals and build the missing total objects.

function getCartesian(object) {
 return Object.entries(object).reduce((r, [k, v]) => {
 var temp = [];
 r.forEach(s =>
 (Array.isArray(v) ? v : [v]).forEach(w =>
 (w && typeof w === 'object' ? getCartesian(w) : [w]).forEach(x =>
 temp.push(Object.assign({}, s, { [k]: x }))
 )
 )
 );
 return temp;
 }, [{}]);
}
function random(min, max) {
 return Math.floor(Math.random() * (max - min)) + min;
}
const
 countries = ['Belgium', 'Uk'],
 years = ['2019', '2018', '2017'],
 colors = ['red', 'orange', 'green'],
 result = getCartesian({ country: countries, year: years })
 .map(o => Object.assign(o, ...colors.map(k => ({ [k]: random(0, 300) })))),
 totals = result.reduce((r, o) => (colors.forEach(color => {
 r[o.year] = r[o.year] || {};
 r[o.year][color] = (r[o.year][color] || 0) + o[color];
 }), r), {});
result.push(...years.map(year => Object.assign(
 { country: 'TOT', year },
 ...colors.map(color => ({ [color]: totals[year][color] }))
)));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

adiga
35.4k9 gold badges66 silver badges88 bronze badges
answered Jan 31, 2019 at 10:34
Sign up to request clarification or add additional context in comments.

1 Comment

That fancy name cartesian though.. nice"
2

You could do something like this using forEach and reduce

  • Get all the possible combination of countries and years in combo
  • reduce the combo and get the sum for each color grouped based on the year
  • concat the combo array and total values grouped from the reduce

const countries = ['Belgium', 'Uk']
const years = ['2019', '2018', '2017']
const colors = ['red', 'orange', 'green']
function random(min, max) {
 return Math.floor(Math.random() * (max - min + 1)) + min;
}
const combo = [];
countries.forEach(country => {
 years.forEach(year => {
 
 const obj = {country,year};
 colors.forEach(color => {
 obj[color] = random(1, 10)
 })
 combo.push(obj)
 })
})
const total = combo.reduce((acc, {year, country,...rest}) => {
 acc[year] = acc[year] || {year,country: 'Tot'};
 for (let color in rest) {
 acc[year][color] = (acc[year][color] + rest[color]) || rest[color]
 }
 return acc;
}, {})
const final = combo.concat(Object.values(total))
console.log(final)

answered Jan 31, 2019 at 10:38

1 Comment

elegant use spread operator, destructor and OR. Save in their recipes. Thanks.
2
const countries = ['Belgium', 'Uk']
const years = ['2019', '2018', '2017']
const colors = ['red', 'orange', 'green']
countries.push('Total');
result = [];
function random(min, max) {
 return Math.floor(Math.random() * (max - min)) + min;
}
countries.forEach(item => {
if (item !== 'Total') {
 yearArr = [];
 years.forEach(y => {
 obj = {
 country: item,
 year: y
 }
 colors.forEach(c => {
 obj[c] = random(0, 300)
 })
 result.push(obj)
 yearArr.push(obj)
 })
 }
})
years.forEach(y => {
 obj = result.filter(({ year, country }) => (year === y && country 
 !== 'total'))
 .reduce((a, b) => ({ country: 'total', year: a.year, red: a.red + 
 b.red, orange: a.orange + b.orange, green: a.green + b.green }))
 result.push(obj)
})
console.log(result);
answered Jan 31, 2019 at 15:22

Comments

0

My solution

const countries = ['Belgium', 'Uk'];
const years = ['2019', '2018', '2017'];
const colors = ['red', 'orange', 'green'];
const min = 0;
const max = 300;
console.log(getTotal(
 getResult(countries, years, colors),
 colors
 ));
function getResult(countries, years, colors) {
 const result = [];
 countries.forEach(country => {
 years.forEach(year => {
 const item = {
 country: country,
 year: year
 };
 colors.forEach(color => {
 item[color] = random(min, max);
 });
 result.push(item);
 });
 });
 return result;
}
function getTotal(data, colors) {
 const total = {};
 data.forEach(item => {
 if (typeof total[item.year] === 'undefined') {
 total[item.year] = {};
 colors.forEach(color => {
 total[item.year][color] = item[color];
 });
 } else {
 colors.forEach(color => {
 total[item.year][color] += item[color];
 });
 }
 });
 for (var totalItem in total) {
 data.push({country: 'Tot', year: totalItem, ...total[totalItem]});
 }
 return data;
}
function random(min, max) {
 return Math.floor(Math.random() * (max - min)) + min;
}

answered Jan 31, 2019 at 10:36

Comments

0

This is how it works:

const countries = ['Belgium', 'Uk']
const years = ['2019', '2018', '2017']
const colors = ['red', 'orange', 'green']
const dataset = [];
countries.forEach((country)=>{
 years.forEach((year)=>{
 const obj = {
 country: country,
 year: year 
 }
 colors.forEach((color)=>{
 obj[color] = Math.floor(Math.random() * 300) + 1;
 })
 dataset.push(obj);
 })
})
console.log(dataset);

https://jsfiddle.net/martinmakesitwork/0x1may62/9/

answered Jan 31, 2019 at 10:29

1 Comment

You forgot the totals
0

Double reduce and spread operator.

For each country and then for each years, we are creating a new json. This json is made of the country + year key and colors we have to loop on aswell. Then we add the total.

function random(min, max) {
 return Math.floor(Math.random() * (max - min)) + min;
}
const countries = ['Belgium', 'Uk'];
const years = ['2019', '2018', '2017'];
const colors = ['red', 'orange', 'green'];
const arr = countries.reduce((tmp, x) => [
 ...tmp,
 ...years.map(y => colors.reduce((json, z) => {
 json[z] = random(0, 255);
 return json;
 }, {
 country: x,
 year: y,
 })),
], []);
// add the Tot
const arrWithTotal = [
 ...arr,
 
 ...years.map(y => colors.reduce((json, x) => {
 json[x] = arr.reduce((tmp, z) => z.year === y ? tmp + z[x] : tmp, 0);
 return json;
 }, {
 country: 'Tot',
 year: y,
 })),
];
console.log(arrWithTotal);

answered Jan 31, 2019 at 11:07

Comments

0

Here's a purely functional solution. It uses local mutation in Array#reduce, but the computation itself is pure:

const ap = fs => xs => xs.flatMap (x => fs.map (f => f (x)))
const lift2 = f => xs => ys => ap (xs.map (f)) (ys)
// source: https://stackoverflow.com/a/1527820/411632
const random = min => max => {
 const min_ = Math.ceil (min)
 const max_ = Math.floor (max)
 
 return Math.floor (Math.random () * (max - min + 1)) + min
}
//////////////////////////////////////////////////////////////
const countries = ['Belgium', 'Uk']
const years = ['2019', '2018', '2017']
const output1 = lift2 (year => country => ({
 country,
 year,
 red: random (1) (255),
 orange: random (1) (255),
 green: random (1) (255) 
})) (years) (countries)
const output2 = Object.values (
 output1.reduce ((o, { year, red, green, orange, ...rest }) => {
 const { 
 red:red_ = 0, 
 green:green_ = 0, 
 orange:orange_ = 0 
 } = o[year] || {}
 
 o[year] = { 
 ...rest,
 country: 'Tot',
 year, 
 red: red + red_, 
 green: green + green_, 
 orange: orange + orange_
 }
 
 return o
 }, {})
)
const output3 = [...output1, ...output2]
console.log (output3)

answered Jan 31, 2019 at 11:03

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.