Javascript create new array from given array - javascript

I have an array of objects containing num and sum
[
{num: 1, sum: 10},
{num: 1, sum: 2},
{num: 1, sum: 3},
{num: 2, sum: 1},
{num: 2, sum: 40},
{num: 2, sum: 100},
{num: 3, sum: 0},
{num: 4, sum: 0},
]
How I want is:
[
{1: {count: 3, totalSum: 15},
{2: {count: 3, totalSum: 141},
{3: {count: 1, totalSum: 0},
{4: {count: 1, totalSum: 0},
]
Any help is highly appreciated

An option is to reduce into an object indexed by nums, and then map that object into an array:
const input=[{num:1,sum:10},{num:1,sum:2},{num:1,sum:3},{num:2,sum:1},{num:2,sum:40},{num:2,sum:100},{num:3,sum:0},{num:4,sum:0},]
const outputObj = input.reduce((a, { num, sum }) => {
if (!a[num]) a[num] = { count: 0, totalSum: 0 };
a[num].count++;
a[num].totalSum += sum;
return a;
}, {});
const output = Object.entries(outputObj)
.map(([key, val]) => ({ [key] : val }));
console.log(output);

You can use reduce method in order to group the array and then use another reduce method in order to find count and totalSum properties for every key.
let arr = [ {num: 1, sum: 10}, {num: 1, sum: 2}, {num: 1, sum: 3}, {num: 2, sum: 1}, {num: 2, sum: 40}, {num: 2, sum: 100}, {num: 3, sum: 0}, {num: 4, sum: 0}, ]
var groupBy = function(arr, key) {
return arr.reduce(function(acc, x) {
(acc[x[key]] = acc[x[key]] || []).push(x);
return acc;
}, {});
};
let grouped = groupBy(arr, 'num');
let result = Object.keys(grouped).reduce(function(acc, key){
let obj ={ [key] : {count : grouped[key].length, totalSum: grouped[key].map(a => a.sum).reduce((a,b) => a + b)}}
acc.push(obj)
return acc;
},[]);
console.log(result);

Related

Return all objects in an array containing consecutive numbers - Javascript

I would like a function in javascript that can return all objects containing consecutive numbers.
For example...
const myObjs = [
{number: 1},
{number: 2},
{number: 3},
{number: 5},
{number: 7},
{number: 10},
{number: 11}
]
Running the function on myObjs should return...
const result = [
{number: 1},
{number: 2},
{number: 3},
{number: 10},
{number: 11}
]
Can anyone help? Thank you
You could check the predecessor or successor object if in sequence.
const
data = [{ number: 1 }, { number: 2 }, { number: 3 }, { number: 5 }, { number: 7 }, { number: 10 }, { number: 11 }],
result = data.filter(({ number }, i, a) =>
a[i - 1]?.number === number - 1 ||
number + 1 === a[i + 1]?.number
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Data wrangling: Sum values from two different arrays in javascript and push a new object

I have two arrays/json files in the following format:
const data2 = [{date: "2018-04-23", a: 2, b: 2},
{date: "2020-04-24", a: 1, b: 2},
{date: "2020-04-25", a: 1, b: 2}]
const data3 = [{date: "2018-04-23", a: 5, b: 2},
{date: "2020-04-24", a: 4, b: 2},
{date: "2020-04-25", a: 1, b: 2}]
and I want to get the following result:
const result = [{date: "2018-04-23", sum: 7},
{date: "2020-04-24", sum: 5},
{date: "2020-04-25", sum: 2}]
what is the best way to calculate/create this new array?
I have tried this:
let result = [];
for( var i=1; i < data3.length; i++) {
result.push({ date: data3[i].date,
sum: data2[i].a + data3[i].a})
}
The issue is that I need to make sure that both data2 and data3 arrays are sorted by date. Is there a better way to do it accounting for the date in the for loop? Any other structure different to a for loop is fine. I would like to know what is the best way to obtain the result array checking by date.
const result = data2.map(item => {
const fItem = data3.find(e=>e.date === item.date)
if(fItem){
return {date: item.date, sum: item.a + fItem.a}
}
return {date: item.date, sum: item.a}
})
I hope this code will useful for you
And I have the other solution is to convert data3 as an object with keys are date and values are an attribute. For example:
const object = data3.reduce((init, item)=> ({...init, [item.date]: item.a}), {})
After that use is the same above
const fItem = data3.find(e=>e.date === item.date)
change to
const fItem = obj[item.date]
you can create map and then sum the values
const data2 = [{
date: "2018-04-23",
a: 2,
b: 2
},
{
date: "2020-04-24",
a: 1,
b: 2
},
{
date: "2020-04-25",
a: 1,
b: 2
}
]
const data3 = [{
date: "2018-04-23",
a: 5,
b: 2
},
{
date: "2020-04-24",
a: 4,
b: 2
},
{
date: "2020-04-25",
a: 1,
b: 2
}
]
const map2 = data2.reduce((acc, cur) => {
acc[cur.date] = cur.a;
return acc;
}, {});
const map3 = data3.reduce((acc, cur) => {
acc[cur.date] = (acc[cur.date] || 0) + cur.a;
return acc;
}, map2);
const result = Object.keys(map3).map(k => {
return {
date: k,
sum: map3[k]
};
});
console.log(result);

How to split an array of objects by arrays Javascript

I have an array of objects, i need to split this array to multiple arrays. If sum of element count <= 500, return these objects in array.
const array = [{idx: 1, count: 100}, {idx: 2, count: 200}, {idx: 3, count: 200}, {idx: 4, count: 100}]
//Expected Result: array of arrays
// [[{idx: 1, count: 100}, {idx: 2, count: 200}, {idx: 3, count: 200}], [{idx: 4, count: 100}]]
You can simply do it using reduce:
const array = [{idx: 1, count: 100}, {idx: 2, count: 200}, {idx: 3, count: 200}, {idx: 4, count: 100}]
const result = array.reduce((carry, item) => {
if (!carry.array.length || carry.count + item.count > 500) {
carry.array.push([item]);
carry.count = item.count;
} else {
carry.array[carry.array.length - 1].push(item);
carry.count += item.count;
}
return carry;
}, {array: [], count: 0}).array;
console.log(result);
This can be solved quite elegantly with generators:
function* groupTill(arr, predicate) {
let acc = [], pred = predicate();
for(const el of arr) {
if(!pred(el)) {
yield acc; acc = []; pred = predicate();
}
acc.push(el);
}
yield acc;
}
const result = [...groupTill(input, (total = 0) => ({ count }) => (total += count) < 500)];
You can use forEach to iterate through array and have two separate variables. One for the result array and another to hold the sum of count
const array = [{idx: 1, count: 100}, {idx: 2, count: 200}, {idx: 3, count: 200}, {idx: 4, count: 100}]
const res = [[]]; //initialize the result array with initial subarray
let count = 0; //initialize the count to zero
//Loop through the elements of array.
array.forEach(x => {
res[res.length - 1].push(x); //Add the the current element to the last sub array
count += x.count //increase the temporary count
//if count exceeds 500
if(count >= 500){
//add another sub array to the end of final array
res.push([]);
//Reset the count to 0
count = 0;
}
});
console.log(res);
You can use reduce with flags ( These are used to track whether we need to increase the index or not )
const array = [{idx: 1, count: 100}, {idx: 2, count: 200}, {idx: 3, count: 200}, {idx: 4, count: 100}]
let splitter = (arr) => {
let index = 0,
total = 0
return arr.reduce((op, inp) => {
if ((total + inp.count) > 500) {
index++;
total = 0;
}
total += inp.count
op[index] = op[index] || []
op[index].push(inp)
return op
}, [])
}
console.log(splitter(array))

In Javascript, how do I tell array.filter() to duplicate element objects in the new array?

So simplified code.
var a = [
{ name: "first", num: 1 },
{ name: "first", num: 2 },
{ name: "first", num: 3 },
{ name: "first", num: 4 },
{ name: "first", num: 5 },
{ name: "first", num: 6 },
{ name: "first", num: 7 },
{ name: "first", num: 8 },
{ name: "first", num: 9 }
];
var b = a.filter(function(el) {
return el.num % 2 == 0;
});
console.log("a1", a); // [1, 20, 3, 40, 5, 60, 7, 80, 9]
console.log("b1", b); // [20, 40, 60, 80]
for (let i = 0; i < b.length; i++) {
b[i].num = b[i].num * 10;
}
console.log("a2", a); // [1, 20, 3, 40, 5, 60, 7, 80, 9]
console.log("b2", b); // [20, 40, 60, 80]
My new understanding is the array element contains a reference to an object, not the object. What are some ways to get those objects duplicated?
Filter, then build new objects from the filtered array and put the new things in a new array?
Use some method I'm not currently familiar with?
Redesign the code to stop using objects in an array?
Also, what's up with console.log() showing the variables have changed when placed before the for loop?
If you wish to duplicate the objects inside the array, you should use the map function.
var b = a.filter(val => val.num %2 === 0).map(val => Object.assign({}, val, { num: val.num * 10}));
The map function will return a new array with the value returned from the function. In this example, we are creating a new object Object.assign({}) and duplicating the existing object while changing the num field.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
If you want to clone objects you will need a clone function, I use this function
const clone = obj =>
Array.isArray(obj)
? obj.map(item => clone(item))
: obj instanceof Date
? new Date(obj.getTime())
: obj && typeof obj === 'object'
? Object.getOwnPropertyNames(obj).reduce((o, prop) => {
o[prop] = clone(obj[prop]);
return o;
}, {})
: obj;
You can then clone the array with
let c = clone(b);
Which will be a new array where each object is a new clone.
var a = [{name: 'first', num:1}, {name:'first', num: 2}, {name:'first', num: 3},
{name:'first', num: 4}, {name:'first', num: 5}, {name:'first', num: 6}, {name:'first', num: 7},
{name:'first', num: 8}, {name:'first', num: 9}];
var b = a.filter(function(el){return el.num%2==0 });
const clone = obj =>
Array.isArray(obj)
? obj.map(item => clone(item))
: obj instanceof Date
? new Date(obj.getTime())
: obj && typeof obj === 'object'
? Object.getOwnPropertyNames(obj).reduce((o, prop) => {
o[prop] = clone(obj[prop]);
return o;
}, {})
: obj;
let c = clone(b);
console.log(b[0] === c[0]);
Yes, elements of Array a are all pointers. so you need to use Object.assign (as many says)
and other solution with array reduce usage (see Adrian Brand comment)
var a = [ { name: 'first', num: 1 }
, { name: 'first', num: 2 }
, { name: 'first', num: 3 }
, { name: 'first', num: 4 }
, { name: 'first', num: 5 }
, { name: 'first', num: 6 }
, { name: 'first', num: 7 }
, { name: 'first', num: 8 }
, { name: 'first', num: 9 }
]
var b = a.filter(el=>!(el.num%2)).map(el=>Object.assign({},el))
// other solution with reduce
var c = a.reduce((acc,cur)=>{
if (!(cur.num%2) )acc.push(Object.assign({},cur))
return acc
}, [])
ConsoleArrayNamNum('var a -1-',a) // [1,2,3,4,5,6,7,8,9]
ConsoleArrayNamNum('var b -1-',b) // [2, 4, 6, 8]
ConsoleArrayNamNum('var c -1-',c) // [2, 4, 6, 8]
for(let elm of b)
{ elm.num *= 10 }
ConsoleArrayNamNum('var a -2-',a) // [1,2,3,4,5,6,7,8,9]
ConsoleArrayNamNum('var b -2-',b) // [20, 40, 60, 80]
function ConsoleArrayNamNum(title,arr) {
console.log(title)
for(let elm of arr)
{ console.log(`{ name: '${elm.name}', num: ${elm.num} }`) }
}
.as-console-wrapper { min-height: 100% !important; }
If you want a new array with the final values you can use reduce to do it all in one go, reduce starts with an accumulator of an empty array and each iteration if it meets the condition it adds a clone with the spread operator overriding the num time 10.
var a = [{name: 'first', num:1}, {name:'first', num: 2}, {name:'first', num: 3},
{name:'first', num: 4}, {name:'first', num: 5}, {name:'first', num: 6}, {name:'first', num: 7},
{name:'first', num: 8}, {name:'first', num: 9}];
const evensTimes10 = array => array.reduce((results, item) => {
if (item.num % 2 === 0) {
results.push({ ...item, num: item.num * 10 });
}
return results;
}, []);
var b = evensTimes10(a);
console.log('a1',a); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log('b1',b); // [20, 40, 60, 80]
A simple solution using some ES6 syntax:
var a = [{name: 'first', num:1}, {name:'first', num: 2}, {name:'first', num: 3},
{name:'first', num: 4}, {name:'first', num: 5}, {name:'first', num: 6}, {name:'first', num: 7},
{name:'first', num: 8}, {name:'first', num: 9}];
const b = a
.filter(el => {
if (el.num % 2 === 0) {
return {
...el
}
}
})
.map(newEl => newEl.num * 10);
console.log('a', a); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log('b', b);
.filter() iterates the "a" array and returns only elements with
"num" property that reaches the condition. This is a cloned array.
return { ...el } returns a cloned object thanks to spread
operator.
.map() creates a new array and returns each "el.num" value *
10
Here some info about .map() .filter() and spread operator:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
I found this very interesting site that lists all Javascript functions with their descriptions and shows if is mutable or not, this helps a lot:
https://doesitmutate.xyz/

Is there any lodash function to achieve this?

I have an array
var a = [
{id: 1, item: 3},
{id: 1, item: 4},
{id: 1, item: 5},
{id: 2, item: 6},
{id: 2, item: 7},
{id: 3, item: 8}
]
I need output like this:
[{id: 1, items: [3, 4, 5]}, {id: 2, items: [6,7]}, {id: 3, items: [8]}]
Here's a solution that first groups by id and then maps across the groupings to get the required collection:
let result = _(a)
.groupBy('id')
.map( (group ,id) => ({id: id, items: _.map(group, 'item')}))
.value()
It's pretty ugly, but then other answers are not pretty either
var a = [
{id: 1, item: 3},
{id: 1, item: 4},
{id: 1, item: 5},
{id: 2, item: 6},
{id: 2, item: 7},
{id: 3, item: 8}
];
var ret = _.chain(a)
.groupBy(elt => elt.id)
.mapValues(elt => _.reduce(elt, (acc, sub) => acc.concat(sub.item),[]))
.map((value, key) => ({id: key, items:value}))
.value();
console.log(ret);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>

Categories

Resources