Even distribution of people to events - javascript

I'm trying to evenly distribute several people to different events. For example:
People Events
John 10.10.2021 (4 people needed)
Jack 11.10.2021 (2 people needed)
Harry 12.10.2021 (1 people needed)
Charlie 13.10.2021 (3 people needed)
Jacob 14.10.2021 (5 people needed)
I want it, so that always the amount of people needed get assigned to the event. In the best case, every person gets assigned the same amount of times.
How can I achieve this in javascript / typescript?

You could use map() to loop through events and add people to them by slicing the array of people. You could add logic for them to be separated evenly.
const people = ["John", "Jack", "Harry", "Charlie", "Jacob"];
const events = [{
date: "10.10.2021",
numPeople: 4
}, {
date: "11.10.2021",
numPeople: 2
}, {
date: "12.10.2021",
numPeople: 1
}, {
date: "13.10.2021",
numPeople: 3
}, {
date: "14.10.2021",
numPeople: 5
}];
const result = events.map((e) => {
return {
...e,
people: people.slice(0, e.numPeople)
}
});
console.log(result);

Like this maybe:
const people = ['John', 'Jack', 'Harry', 'Charlie', 'Jacob']
const events = [{date: '10.10.2021', nr: 4}, {date: '11.10.2021', nr: 2},
{date: '12.10.2021', nr: 1}, {date: '13.10.2021', nr: 3}, {date: '14.10.2021', nr: 5}]
const ppl = []
people.forEach(p => ppl.push({'name': p, 'assigned': 0}))
const arr = []
events.forEach(e => {
let ex = {[e.date]: []}
arr.push(ex)
for(let i = 0; i < e.nr; i++) {
let min = Math.min(...ppl.map(p => p.assigned))
let pe = ppl.filter(p => p.assigned === min)
ex[e.date].push(pe[0].name)
pe[0].assigned++
}
})
console.log(...arr)

Related

Summing object values per each key and converting the result into a map [duplicate]

I have something like this:
$scope.traveler = [
{ description: 'Senior', Amount: 50},
{ description: 'Senior', Amount: 50},
{ description: 'Adult', Amount: 75},
{ description: 'Child', Amount: 35},
{ description: 'Infant', Amount: 25 },
];
Now to have a total Amount of this array I'm doing something like this:
$scope.totalAmount = function(){
var total = 0;
for (var i = 0; i < $scope.traveler.length; i++) {
total = total + $scope.traveler[i].Amount;
}
return total;
}
It's easy when is only one array, but I have others arrays with a different property name that I would like to sum.
I would be happier If I could do something like this:
$scope.traveler.Sum({ Amount });
But I don't know how to go through this in a way that I could reuse it in the future like this:
$scope.someArray.Sum({ someProperty });
I know that this question has an accepted answer but I thought I'd chip in with an alternative which uses array.reduce, seeing that summing an array is the canonical example for reduce:
$scope.sum = function(items, prop){
return items.reduce( function(a, b){
return a + b[prop];
}, 0);
};
$scope.travelerTotal = $scope.sum($scope.traveler, 'Amount');
Fiddle
Just another take, this is what native JavaScript functions Map and Reduce were built for (Map and Reduce are powerhouses in many languages).
var traveler = [{description: 'Senior', Amount: 50},
{description: 'Senior', Amount: 50},
{description: 'Adult', Amount: 75},
{description: 'Child', Amount: 35},
{description: 'Infant', Amount: 25}];
function amount(item){
return item.Amount;
}
function sum(prev, next){
return prev + next;
}
traveler.map(amount).reduce(sum);
// => 235;
// or use arrow functions
traveler.map(item => item.Amount).reduce((prev, next) => prev + next);
Note: by making separate smaller functions we get the ability to use them again.
// Example of reuse.
// Get only Amounts greater than 0;
// Also, while using Javascript, stick with camelCase.
// If you do decide to go against the standards,
// then maintain your decision with all keys as in...
// { description: 'Senior', Amount: 50 }
// would be
// { Description: 'Senior', Amount: 50 };
var travelers = [{description: 'Senior', amount: 50},
{description: 'Senior', amount: 50},
{description: 'Adult', amount: 75},
{description: 'Child', amount: 35},
{description: 'Infant', amount: 0 }];
// Directly above Travelers array I changed "Amount" to "amount" to match standards.
function amount(item){
return item.amount;
}
travelers.filter(amount);
// => [{description: 'Senior', amount: 50},
// {description: 'Senior', amount: 50},
// {description: 'Adult', amount: 75},
// {description: 'Child', amount: 35}];
// Does not include "Infant" as 0 is falsey.
Updated Answer
Due to all the downsides of adding a function to the Array prototype, I am updating this answer to provide an alternative that keeps the syntax similar to the syntax originally requested in the question.
class TravellerCollection extends Array {
sum(key) {
return this.reduce((a, b) => a + (b[key] || 0), 0);
}
}
const traveler = new TravellerCollection(...[
{ description: 'Senior', Amount: 50},
{ description: 'Senior', Amount: 50},
{ description: 'Adult', Amount: 75},
{ description: 'Child', Amount: 35},
{ description: 'Infant', Amount: 25 },
]);
console.log(traveler.sum('Amount')); //~> 235
Original Answer
Since it is an array you could add a function to the Array prototype.
traveler = [
{ description: 'Senior', Amount: 50},
{ description: 'Senior', Amount: 50},
{ description: 'Adult', Amount: 75},
{ description: 'Child', Amount: 35},
{ description: 'Infant', Amount: 25 },
];
Array.prototype.sum = function (prop) {
var total = 0
for ( var i = 0, _len = this.length; i < _len; i++ ) {
total += this[i][prop]
}
return total
}
console.log(traveler.sum("Amount"))
The Fiddle: http://jsfiddle.net/9BAmj/
Use reduce with destructuring to sum Amount:
const traveler = [
{ description: 'Senior', Amount: 50 },
{ description: 'Senior', Amount: 50 },
{ description: 'Adult', Amount: 75 },
{ description: 'Child', Amount: 35 },
{ description: 'Infant', Amount: 25 },
];
console.log(traveler.reduce((n, {Amount}) => n + Amount, 0));
I always avoid changing prototype method and adding library so this is my solution:
Using reduce Array prototype method is sufficient
// + operator for casting to Number
items.reduce((a, b) => +a + +b.price, 0);
Alternative for improved readability and using Map and Reduce:
const traveler = [
{ description: 'Senior', amount: 50 },
{ description: 'Senior', amount: 50 },
{ description: 'Adult', amount: 75 },
{ description: 'Child', amount: 35 },
{ description: 'Infant', amount: 25 },
];
const sum = traveler
.map(item => item.amount)
.reduce((prev, curr) => prev + curr, 0);
Re-useable function:
const calculateSum = (obj, field) => obj
.map(items => items.attributes[field])
.reduce((prev, curr) => prev + curr, 0);
It's working for me in TypeScript and JavaScript:
let lst = [
{ description:'Senior', price: 10},
{ description:'Adult', price: 20},
{ description:'Child', price: 30}
];
let sum = lst.map(o => o.price).reduce((a, c) => { return a + c });
console.log(sum);
I hope is useful.
I thought I'd drop my two cents on this: this is one of those operations that should always be purely functional, not relying on any external variables. A few already gave a good answer, using reduce is the way to go here.
Since most of us can already afford to use ES2015 syntax, here's my proposition:
const sumValues = (obj) => Object.keys(obj).reduce((acc, value) => acc + obj[value], 0);
We're making it an immutable function while we're at it. What reduce is doing here is simply this:
Start with a value of 0 for the accumulator, and add the value of the current looped item to it.
Yay for functional programming and ES2015! :)
You can do the following:
$scope.traveler.map(o=>o.Amount).reduce((a,c)=>a+c);
I'm not sure this has been mentioned yet. But there is a lodash function for that. Snippet below where value is your attribute to sum is 'value'.
_.sumBy(objects, 'value');
_.sumBy(objects, function(o) { return o.value; });
Both will work.
From array of objects
function getSum(array, column)
let values = array.map((item) => parseInt(item[column]) || 0)
return values.reduce((a, b) => a + b)
}
foo = [
{ a: 1, b: "" },
{ a: null, b: 2 },
{ a: 1, b: 2 },
{ a: 1, b: 2 },
]
getSum(foo, a) == 3
getSum(foo, b) == 6
can also use Array.prototype.forEach()
let totalAmount = 0;
$scope.traveler.forEach( data => totalAmount = totalAmount + data.Amount);
return totalAmount;
Here is a one-liner using ES6 arrow functions.
const sumPropertyValue = (items, prop) => items.reduce((a, b) => a + b[prop], 0);
// usage:
const cart_items = [ {quantity: 3}, {quantity: 4}, {quantity: 2} ];
const cart_total = sumPropertyValue(cart_items, 'quantity');
After going through these answers I think that actually a for (or forEach or for of with await) loop is much more readable that than reduce or even map and reduce.
Think of:
coming back to this code after 6 months or maintaining this by someone else. I think your approach of using a loop is good enough.
extending this function in the future, in case you might want to add a currency conversion or similar. Doing this in a one-liner is not a great idea.
var traveler = [
{Amount: 50, description: 'Senior'},
{Amount: 50, description: 'Senior'},
{Amount: 75, description: 'Adult'},
{Amount: 35, description: 'Child'},
{Amount: 25, description: 'Infant'}
];
var sumFromArray = (propertyName, array) => {
let sum = 0;
array.forEach(item => {
sum += item[propertyName] ?? 0;
});
return sum;
};
var sumOfTraveler = sumFromArray('Amount', traveler);
console.log(sumOfTraveler);
Using types your function definition might look like:
const sumFromArray = (propertyName: string, array: Array<{[propertyName: string]: number}>) => { ... };
See here for more details: TypeScript A computed property name in a type literal must directly refer to a built-in symbol
I have nothing against map, reduce or one-liners, this is just food for thought.
How to sum array of object using Javascript
const traveler = [
{ description: 'Senior', Amount: 50},
{ description: 'Senior', Amount: 50},
{ description: 'Adult', Amount: 75},
{ description: 'Child', Amount: 35},
{ description: 'Infant', Amount: 25 }
];
const traveler = [
{ description: 'Senior', Amount: 50},
{ description: 'Senior', Amount: 50},
{ description: 'Adult', Amount: 75},
{ description: 'Child', Amount: 35},
{ description: 'Infant', Amount: 25 },
];
function sum(arrayData, key){
return arrayData.reduce((a,b) => {
return {Amount : a.Amount + b.Amount}
})
}
console.log(sum(traveler))
`
Here's a solution I find more flexible:
function sumOfArrayWithParameter (array, parameter) {
let sum = null;
if (array && array.length > 0 && typeof parameter === 'string') {
sum = 0;
for (let e of array) if (e && e.hasOwnProperty(parameter)) sum += e[parameter];
}
return sum;
}
To get the sum, simply use it like that:
let sum = sumOfArrayWithParameter(someArray, 'someProperty');
You can use Array.prototype.reduce:
const sum = traveler.reduce((acc , val)=>{
return acc + val.amount;
} ,0);
I was already using jquery. But I think its intuitive enough to just have:
var total_amount = 0;
$.each(traveler, function( i, v ) { total_amount += v.Amount ; });
This is basically just a short-hand version of #akhouri's answer.
You can use jscollection library for database like query job easily in just one line
https://github.com/somnathpanja/jscollection
var total = List.extend(traveler).select('Amount').sum();
i honestly got frustrated while reading all the code that where posted as a solution to this cus i'm a new be and i'n trying to add a functionality to a simple app for practice. The simple way to solve this is
let testArray = [5, 7, 8, 4];
function(){
sum = 0;
for(let i = 0; i < testArray.length; i++){
sum += testArray[i];
}
// will give you the sum of the array

Build an array of arrays from an array using REDUCE in javascript [duplicate]

This question already has answers here:
How to get all properties values of a JavaScript Object (without knowing the keys)?
(25 answers)
Closed 10 months ago.
Input data
const users = [{id: 1, name: 'Madou', age: 37},
{id: 2, name: 'Fatoumata', age: 33},
{id: 3, name: 'Amadou', age: 31}];
Output
[ [ 1, 'Madou', 37 ], [ 2, 'Fatoumata', 33 ], [ 3, 'Amadou', 31 ] ]
I implemented this:
const _data = users.map((item) => {
return Object.keys(item).map((value) => item[value]);
});
console.log(_data);
But I want to use REDUCE instead, but I don't have any how to do it
You can use Array.map() as follows:
const users = [{id: 1, name: 'Madou', age: 37},
{id: 2, name: 'Fatoumata', age: 33},
{id: 3, name: 'Amadou', age: 31}];
const result = users.map(o => Object.values(o));
console.log(result);
const _data = users.reduce((acc, { id, name, age }) => {
acc.push([id, name, age]);
return acc;
}, []);

declarative loop vs imperative loop

I am trying to switch my programming style to declarative from imperative, but there is some concept that is bugging me like the performance when it comes to the loop. For example, I have an original DATA, and after manipulating it I wish to get 3 expected outcomes: itemsHash, namesHash, rangeItemsHash
// original data
const DATA = [
{id: 1, name: 'Alan', date: '2021-01-01', age: 0},
{id: 2, name: 'Ben', date: '1980-02-02', age: 41},
{id: 3, name: 'Clara', date: '1959-03-03', age: 61},
]
...
// expected outcome
// itemsHash => {
// 1: {id: 1, name: 'Alan', date: '2021-01-01', age: 0},
// 2: {id: 2, name: 'Ben', date: '1980-02-02', age: 41},
// 3: {id: 3, name: 'Clara', date: '1959-03-03', age: 61},
// }
// namesHash => {1: 'Alan', 2: 'Ben', 3: 'Clara'}
// rangeItemsHash => {
// minor: [{id: 1, name: 'Alan', date: '2021-01-01', age: 0}],
// junior: [{id: 2, name: 'Ben', date: '1980-02-02', age: 41}],
// senior: [{id: 3, name: 'Clara', date: '1959-03-03', age: 61}],
// }
// imperative way
const itemsHash = {}
const namesHash = {}
const rangeItemsHash = {}
DATA.forEach(person => {
itemsHash[person.id] = person;
namesHash[person.id] = person.name;
if (person.age > 60){
if (typeof rangeItemsHash['senior'] === 'undefined'){
rangeItemsHash['senior'] = []
}
rangeItemsHash['senior'].push(person)
}
else if (person.age > 21){
if (typeof rangeItemsHash['junior'] === 'undefined'){
rangeItemsHash['junior'] = []
}
rangeItemsHash['junior'].push(person)
}
else {
if (typeof rangeItemsHash['minor'] === 'undefined'){
rangeItemsHash['minor'] = []
}
rangeItemsHash['minor'].push(person)
}
})
// declarative way
const itemsHash = R.indexBy(R.prop('id'))(DATA);
const namesHash = R.compose(R.map(R.prop('name')),R.indexBy(R.prop('id')))(DATA);
const gt21 = R.gt(R.__, 21);
const lt60 = R.lte(R.__, 60);
const isMinor = R.lt(R.__, 21);
const isJunior = R.both(gt21, lt60);
const isSenior = R.gt(R.__, 60);
const groups = {minor: isMinor, junior: isJunior, senior: isSenior };
const rangeItemsHash = R.map((method => R.filter(R.compose(method, R.prop('age')))(DATA)))(groups)
To achieve the expected outcome, imperative only loops once while declarative loops at least 3 times(itemsHash,namesHash ,rangeItemsHash ). Which one is better? Is there any trade-off on performance?
I have several responses to this.
First, have you tested to know that performance is a problem? Far too much performance work is done on code that is not even close to being a bottleneck in an application. This often happens at the expense of code simplicity and clarity. So my usual rule is to write the simple and obvious code first, trying not to be stupid about performance, but never worrying overmuch about it. Then, if my application is unacceptably slow, benchmark it to find what parts are causing the largest issues, then optimize those. I've rarely had those places be the equivalent of looping three times rather than one. But of course it could happen.
If it does, and you really need to do this in a single loop, it's not terribly difficult to do this on top of a reduce call. We could write something like this:
// helper function
const ageGroup = ({age}) => age > 60 ? 'senior' : age > 21 ? 'junior' : 'minor'
// main function
const convert = (people) =>
people.reduce (({itemsHash, namesHash , rangeItemsHash}, person, _, __, group = ageGroup (person)) => ({
itemsHash: {...itemsHash, [person .id]: person},
namesHash: {...namesHash, [person .id]: person.name},
rangeItemsHash: {...rangeItemsHash, [group]: [...(rangeItemsHash [group] || []), person]}
}), {itemsHash: {}, namesHash: {}, rangeItemsHash: {}})
// sample data
const data = [{id: 1, name: 'Alan', date: '2021-01-01', age: 0}, {id: 2, name: 'Ben', date: '1980-02-02', age: 41}, {id: 3, name: 'Clara', date: '1959-03-03', age: 61}]
// demo
console .log (JSON .stringify (
convert (data)
, null, 4))
.as-console-wrapper {max-height: 100% !important; top: 0}
(You can remove the JSON .stringify call to demonstrate that the references are shared between the various output hashes.)
There are two directions I might go from here to clean up this code.
The first would be to use Ramda. It has some functions that would help simplify a few things here. Using R.reduce, we could eliminate the annoying placeholder parameters that I use to allow me to add the default parameter group to the reduce signature, and maintain expressions-over-statements style coding. (We could alternatively do something with R.call.) And using evolve together with functions like assoc and over, we can make this more declarative like this:
// helper function
const ageGroup = ({age}) => age > 60 ? 'senior' : age > 21 ? 'junior' : 'minor'
// main function
const convert = (people) =>
reduce (
(acc, person, group = ageGroup (person)) => evolve ({
itemsHash: assoc (person.id, person),
namesHash: assoc (person.id, person.name),
rangeItemsHash: over (lensProp (group), append (person))
}) (acc), {itemsHash: {}, namesHash: {}, rangeItemsHash: {minor: [], junior: [], senior: []}},
people
)
// sample data
const data = [{id: 1, name: 'Alan', date: '2021-01-01', age: 0}, {id: 2, name: 'Ben', date: '1980-02-02', age: 41}, {id: 3, name: 'Clara', date: '1959-03-03', age: 61}]
// demo
console .log (JSON .stringify (
convert (data)
, null, 4))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js"></script>
<script> const {reduce, evolve, assoc, over, lensProp, append} = R </script>
A slight downside to this version over the previous one is the need to predefine the categories senior, junior, and minor in the accumulator. We could certainly write an alternative to lensProp that somehow deals with default values, but that would take us further afield.
The other direction I might go is to note that there is still one potentially serious performance problem in the code, one Rich Snapp called the reduce ({...spread}) anti-pattern. To solve that, we might want to mutate our accumulator object in the reduce callback. Ramda -- by its very philosophic nature -- will not help you with this. But we can define some helper functions that will clean our code up at the same time we address this issue, with something like this:
// utility functions
const push = (x, xs) => ((xs .push (x)), x)
const put = (k, v, o) => ((o[k] = v), o)
const appendTo = (k, v, o) => put (k, push (v, o[k] || []), o)
// helper function
const ageGroup = ({age}) => age > 60 ? 'senior' : age > 21 ? 'junior' : 'minor'
// main function
const convert = (people) =>
people.reduce (({itemsHash, namesHash , rangeItemsHash}, person, _, __, group = ageGroup(person)) => ({
itemsHash: put (person.id, person, itemsHash),
namesHash: put (person.id, person.name, namesHash),
rangeItemsHash: appendTo (group, person, rangeItemsHash)
}), {itemsHash: {}, namesHash: {}, rangeItemsHash: {}})
// sample data
const data = [{id: 1, name: 'Alan', date: '2021-01-01', age: 0}, {id: 2, name: 'Ben', date: '1980-02-02', age: 41}, {id: 3, name: 'Clara', date: '1959-03-03', age: 61}]
// demo
console .log (JSON .stringify (
convert (data)
, null, 4))
.as-console-wrapper {max-height: 100% !important; top: 0}
But in the end, as already suggested, I would not do this unless performance was provably a problem. I think it's much nicer with Ramda code like this:
const ageGroup = ({age}) => age > 60 ? 'senior' : age > 21 ? 'junior' : 'minor'
const convert = applySpec ({
itemsHash: indexBy (prop ('id')),
nameHash: compose (fromPairs, map (props (['id', 'name']))),
rangeItemsHash: groupBy (ageGroup)
})
const data = [{id: 1, name: 'Alan', date: '2021-01-01', age: 0}, {id: 2, name: 'Ben', date: '1980-02-02', age: 41}, {id: 3, name: 'Clara', date: '1959-03-03', age: 61}]
console .log (JSON .stringify(
convert (data)
, null, 4))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js"></script>
<script> const {applySpec, indexBy, prop, compose, fromPairs, map, props, groupBy} = R </script>
Here we might want -- for consistency's sake -- to make ageGroup point-free and/or inline it in the main function. That's not hard, and another answer gave an example of that. I personally find it more readable like this. (There's also probably a cleaner version of namesHash, but I'm out of time.)
This version loops three times, exactly what you are worried about. There are times when that might be a problem. But I wouldn't spend much effort on that unless it's a demonstrable problem. Clean code is a useful goal on its own.
Similar how to .map(f).map(g) == .map(compose(g, f)), you can compose reducers to ensure a single pass gives you all results.
Writing declarative code does not really have anything to do with the decision to loop once or multiple times.
// Reducer logic for all 3 values you're interested in
// id: person
const idIndexReducer = (idIndex, p) =>
({ ...idIndex, [p.id]: p });
// id: name
const idNameIndexReducer = (idNameIndex, p) =>
({ ...idNameIndex, [p.id]: p.name });
// Age
const ageLabel = ({ age }) => age > 60 ? "senior" : age > 40 ? "medior" : "junior";
const ageGroupReducer = (ageGroups, p) => {
const ageKey = ageLabel(p);
return {
...ageGroups,
[ageKey]: (ageGroups[ageKey] || []).concat(p)
}
}
// Combine the reducers
const seed = { idIndex: {}, idNameIndex: {}, ageGroups: {} };
const reducer = ({ idIndex, idNameIndex, ageGroups }, p) => ({
idIndex: idIndexReducer(idIndex, p),
idNameIndex: idNameIndexReducer(idNameIndex, p),
ageGroups: ageGroupReducer(ageGroups, p)
})
const DATA = [
{id: 1, name: 'Alan', date: '2021-01-01', age: 0},
{id: 2, name: 'Ben', date: '1980-02-02', age: 41},
{id: 3, name: 'Clara', date: '1959-03-03', age: 61},
]
// Loop once
console.log(
JSON.stringify(DATA.reduce(reducer, seed), null, 2)
);
Subjective part: Whether it's worth it? I don't think so. I like simple code, and in my own experience going from 1 to 3 loops when working with limited data sets usually is unnoticeable.
So, if using Ramda, I'd stick to:
const { prop, indexBy, map, groupBy, pipe } = R;
const DATA = [
{id: 1, name: 'Alan', date: '2021-01-01', age: 0},
{id: 2, name: 'Ben', date: '1980-02-02', age: 41},
{id: 3, name: 'Clara', date: '1959-03-03', age: 61},
];
const byId = indexBy(prop("id"), DATA);
const nameById = map(prop("name"), byId);
const ageGroups = groupBy(
pipe(
prop("age"),
age => age > 60 ? "senior" : age > 40 ? "medior" : "junior"
),
DATA
);
console.log(JSON.stringify({ byId, nameById, ageGroups }, null, 2))
<script src="https://cdn.jsdelivr.net/npm/ramda#0.27.1/dist/ramda.min.js"></script>

Group array of objects by multiple properties with Lodash

is possible with lodash library group elements by 2 properties?
I have array of objects like this:
[{
id: 1,
amount: 2000,
date: "2018-01-31T00:00:00.000Z"
},{
id: 2,
amount: 3000,
date: "2017-07-31T00:00:00.000Z"
},{
id: 3,
amount: 6000,
date: "2018-01-31T00:00:00.000Z"
},{
id: 4,
amount: 7000,
date: "2017-01-31T00:00:00.000Z"
},{
id: 5,
amount: 5000,
date: "2017-03-31T00:00:00.000Z"
},{
id: 6,
amount: 3000,
date: "2018-02-22T00:00:00.000Z"
},{
id: 7,
amount: 4500,
date: "2017-01-31T00:00:00.000Z"
}]
My goal is group objects in array by:
year
month
Purpose of that grouping is that I need in result order these objects' sum of amount by newest. So for that reason I need distinguish January 2017 from January 2018. It should be 2 different groups.
I am not sure if my approach is correct so I write here my required output:
[
3000, // sum of 2018-2
8000, // sum of 2018-1
3000 // sum of 2017-7
5000 // sum of 2017-3
11500 // sum of 2017-1
]
I tried following command but it doesn't work and give me error:
let xxx = _(data)
.groupBy(function(i) {
new Date(i.date).getFullYear()
})
.groupBy(function(i) {
new Date(i.date).getMonth()
})
.map(x => x.amount)
.sum()
.orderBy('date').value();
Can you help me to fix it ? Thanks.
You can just concat your year and month with groupBy and use it.
var grouped = _.groupBy(data, function(i) {
return new Date(i.date).getFullYear()+'-'+new Date(i.date).getMonth()
})
var resultUnsorted = _.map(t, (val, key) => ({key: key, val: val.reduce((p, c) => c.amount + p, 0) }));
then sort using _.orderBy
const output = _.orderBy(resultUnsorted, 'key');
you can write your custom sort function using the behaviour you want.

Better way to sum a property value in an array

I have something like this:
$scope.traveler = [
{ description: 'Senior', Amount: 50},
{ description: 'Senior', Amount: 50},
{ description: 'Adult', Amount: 75},
{ description: 'Child', Amount: 35},
{ description: 'Infant', Amount: 25 },
];
Now to have a total Amount of this array I'm doing something like this:
$scope.totalAmount = function(){
var total = 0;
for (var i = 0; i < $scope.traveler.length; i++) {
total = total + $scope.traveler[i].Amount;
}
return total;
}
It's easy when is only one array, but I have others arrays with a different property name that I would like to sum.
I would be happier If I could do something like this:
$scope.traveler.Sum({ Amount });
But I don't know how to go through this in a way that I could reuse it in the future like this:
$scope.someArray.Sum({ someProperty });
I know that this question has an accepted answer but I thought I'd chip in with an alternative which uses array.reduce, seeing that summing an array is the canonical example for reduce:
$scope.sum = function(items, prop){
return items.reduce( function(a, b){
return a + b[prop];
}, 0);
};
$scope.travelerTotal = $scope.sum($scope.traveler, 'Amount');
Fiddle
Just another take, this is what native JavaScript functions Map and Reduce were built for (Map and Reduce are powerhouses in many languages).
var traveler = [{description: 'Senior', Amount: 50},
{description: 'Senior', Amount: 50},
{description: 'Adult', Amount: 75},
{description: 'Child', Amount: 35},
{description: 'Infant', Amount: 25}];
function amount(item){
return item.Amount;
}
function sum(prev, next){
return prev + next;
}
traveler.map(amount).reduce(sum);
// => 235;
// or use arrow functions
traveler.map(item => item.Amount).reduce((prev, next) => prev + next);
Note: by making separate smaller functions we get the ability to use them again.
// Example of reuse.
// Get only Amounts greater than 0;
// Also, while using Javascript, stick with camelCase.
// If you do decide to go against the standards,
// then maintain your decision with all keys as in...
// { description: 'Senior', Amount: 50 }
// would be
// { Description: 'Senior', Amount: 50 };
var travelers = [{description: 'Senior', amount: 50},
{description: 'Senior', amount: 50},
{description: 'Adult', amount: 75},
{description: 'Child', amount: 35},
{description: 'Infant', amount: 0 }];
// Directly above Travelers array I changed "Amount" to "amount" to match standards.
function amount(item){
return item.amount;
}
travelers.filter(amount);
// => [{description: 'Senior', amount: 50},
// {description: 'Senior', amount: 50},
// {description: 'Adult', amount: 75},
// {description: 'Child', amount: 35}];
// Does not include "Infant" as 0 is falsey.
Updated Answer
Due to all the downsides of adding a function to the Array prototype, I am updating this answer to provide an alternative that keeps the syntax similar to the syntax originally requested in the question.
class TravellerCollection extends Array {
sum(key) {
return this.reduce((a, b) => a + (b[key] || 0), 0);
}
}
const traveler = new TravellerCollection(...[
{ description: 'Senior', Amount: 50},
{ description: 'Senior', Amount: 50},
{ description: 'Adult', Amount: 75},
{ description: 'Child', Amount: 35},
{ description: 'Infant', Amount: 25 },
]);
console.log(traveler.sum('Amount')); //~> 235
Original Answer
Since it is an array you could add a function to the Array prototype.
traveler = [
{ description: 'Senior', Amount: 50},
{ description: 'Senior', Amount: 50},
{ description: 'Adult', Amount: 75},
{ description: 'Child', Amount: 35},
{ description: 'Infant', Amount: 25 },
];
Array.prototype.sum = function (prop) {
var total = 0
for ( var i = 0, _len = this.length; i < _len; i++ ) {
total += this[i][prop]
}
return total
}
console.log(traveler.sum("Amount"))
The Fiddle: http://jsfiddle.net/9BAmj/
Use reduce with destructuring to sum Amount:
const traveler = [
{ description: 'Senior', Amount: 50 },
{ description: 'Senior', Amount: 50 },
{ description: 'Adult', Amount: 75 },
{ description: 'Child', Amount: 35 },
{ description: 'Infant', Amount: 25 },
];
console.log(traveler.reduce((n, {Amount}) => n + Amount, 0));
I always avoid changing prototype method and adding library so this is my solution:
Using reduce Array prototype method is sufficient
// + operator for casting to Number
items.reduce((a, b) => +a + +b.price, 0);
Alternative for improved readability and using Map and Reduce:
const traveler = [
{ description: 'Senior', amount: 50 },
{ description: 'Senior', amount: 50 },
{ description: 'Adult', amount: 75 },
{ description: 'Child', amount: 35 },
{ description: 'Infant', amount: 25 },
];
const sum = traveler
.map(item => item.amount)
.reduce((prev, curr) => prev + curr, 0);
Re-useable function:
const calculateSum = (obj, field) => obj
.map(items => items.attributes[field])
.reduce((prev, curr) => prev + curr, 0);
It's working for me in TypeScript and JavaScript:
let lst = [
{ description:'Senior', price: 10},
{ description:'Adult', price: 20},
{ description:'Child', price: 30}
];
let sum = lst.map(o => o.price).reduce((a, c) => { return a + c });
console.log(sum);
I hope is useful.
I thought I'd drop my two cents on this: this is one of those operations that should always be purely functional, not relying on any external variables. A few already gave a good answer, using reduce is the way to go here.
Since most of us can already afford to use ES2015 syntax, here's my proposition:
const sumValues = (obj) => Object.keys(obj).reduce((acc, value) => acc + obj[value], 0);
We're making it an immutable function while we're at it. What reduce is doing here is simply this:
Start with a value of 0 for the accumulator, and add the value of the current looped item to it.
Yay for functional programming and ES2015! :)
You can do the following:
$scope.traveler.map(o=>o.Amount).reduce((a,c)=>a+c);
I'm not sure this has been mentioned yet. But there is a lodash function for that. Snippet below where value is your attribute to sum is 'value'.
_.sumBy(objects, 'value');
_.sumBy(objects, function(o) { return o.value; });
Both will work.
can also use Array.prototype.forEach()
let totalAmount = 0;
$scope.traveler.forEach( data => totalAmount = totalAmount + data.Amount);
return totalAmount;
From array of objects
function getSum(array, column)
let values = array.map((item) => parseInt(item[column]) || 0)
return values.reduce((a, b) => a + b)
}
foo = [
{ a: 1, b: "" },
{ a: null, b: 2 },
{ a: 1, b: 2 },
{ a: 1, b: 2 },
]
getSum(foo, a) == 3
getSum(foo, b) == 6
Here is a one-liner using ES6 arrow functions.
const sumPropertyValue = (items, prop) => items.reduce((a, b) => a + b[prop], 0);
// usage:
const cart_items = [ {quantity: 3}, {quantity: 4}, {quantity: 2} ];
const cart_total = sumPropertyValue(cart_items, 'quantity');
After going through these answers I think that actually a for (or forEach or for of with await) loop is much more readable that than reduce or even map and reduce.
Think of:
coming back to this code after 6 months or maintaining this by someone else. I think your approach of using a loop is good enough.
extending this function in the future, in case you might want to add a currency conversion or similar. Doing this in a one-liner is not a great idea.
var traveler = [
{Amount: 50, description: 'Senior'},
{Amount: 50, description: 'Senior'},
{Amount: 75, description: 'Adult'},
{Amount: 35, description: 'Child'},
{Amount: 25, description: 'Infant'}
];
var sumFromArray = (propertyName, array) => {
let sum = 0;
array.forEach(item => {
sum += item[propertyName] ?? 0;
});
return sum;
};
var sumOfTraveler = sumFromArray('Amount', traveler);
console.log(sumOfTraveler);
Using types your function definition might look like:
const sumFromArray = (propertyName: string, array: Array<{[propertyName: string]: number}>) => { ... };
See here for more details: TypeScript A computed property name in a type literal must directly refer to a built-in symbol
I have nothing against map, reduce or one-liners, this is just food for thought.
You can use Array.prototype.reduce:
const sum = traveler.reduce((acc , val)=>{
return acc + val.amount;
} ,0);
How to sum array of object using Javascript
const traveler = [
{ description: 'Senior', Amount: 50},
{ description: 'Senior', Amount: 50},
{ description: 'Adult', Amount: 75},
{ description: 'Child', Amount: 35},
{ description: 'Infant', Amount: 25 }
];
const traveler = [
{ description: 'Senior', Amount: 50},
{ description: 'Senior', Amount: 50},
{ description: 'Adult', Amount: 75},
{ description: 'Child', Amount: 35},
{ description: 'Infant', Amount: 25 },
];
function sum(arrayData, key){
return arrayData.reduce((a,b) => {
return {Amount : a.Amount + b.Amount}
})
}
console.log(sum(traveler))
`
Here's a solution I find more flexible:
function sumOfArrayWithParameter (array, parameter) {
let sum = null;
if (array && array.length > 0 && typeof parameter === 'string') {
sum = 0;
for (let e of array) if (e && e.hasOwnProperty(parameter)) sum += e[parameter];
}
return sum;
}
To get the sum, simply use it like that:
let sum = sumOfArrayWithParameter(someArray, 'someProperty');
I was already using jquery. But I think its intuitive enough to just have:
var total_amount = 0;
$.each(traveler, function( i, v ) { total_amount += v.Amount ; });
This is basically just a short-hand version of #akhouri's answer.
You can use jscollection library for database like query job easily in just one line
https://github.com/somnathpanja/jscollection
var total = List.extend(traveler).select('Amount').sum();
i honestly got frustrated while reading all the code that where posted as a solution to this cus i'm a new be and i'n trying to add a functionality to a simple app for practice. The simple way to solve this is
let testArray = [5, 7, 8, 4];
function(){
sum = 0;
for(let i = 0; i < testArray.length; i++){
sum += testArray[i];
}
// will give you the sum of the array

Categories

Resources