ES6/7 equivalent to underscore’s range function - javascript

Is there any ECMAScript 6/7 equivalent to underscore’s range function?
In underscore:
_.range(startPage, endPage + 1);
In ES2015:
Array.from(Array(n), (_, i) => x + i)
Not sure how the ES2015 version works. I would like to know how range in ecmascript of javascript works

The idea is to create an array of length end - start + 1, and then fill it with the relevant numbers using Array#from.
The Array.from() method creates a new Array instance from an
array-like or iterable object.
In this case Array#from needs an object with the length property. Using Array(n) creates such an object (array). You can also use { length: n } directly. In this case n = Math.abs(end - start) + 1.
Array#from accepts a mapFn callback, a function that can transform the iterated value. The function receives 2 params - the values (which we can ignore in this case), and the index (0 based). Adding start to the current index will create the numbers in the range.
const range = (start, end) => Array.from(
Array(Math.abs(end - start) + 1),
(_, i) => start + i
);
console.log(range(5, 10));
console.log(range(-10, -5));
console.log(range(-5, 10));
This version will handle reverse range as well (large to small) as well:
const range = (start, end) => {
const inc = (end - start) / Math.abs(end - start);
return Array.from(
Array(Math.abs(end - start) + 1),
(_, i) => start + i * inc
);
};
console.log(range(5, 10));
console.log(range(-5, -10));
console.log(range(10, -5));

Note that the following implementation does not allow for lazy-generated lists:
Array.from(Array(n), (_, i) => x + i)
Imagine that you need a list of 1M numbers:
range(1, 1000000);
Are you going to consume all of them? Maybe not, yet all the numbers have been generated already and they probably left a non-trivial footprint on your memory.
It would be nice if we could get them one by one on demand.
It turns out we can do exactly that with a generator:
function* range(start, end, step = 1) {
for (let value = start; value < end; value += step) {
yield value;
}
}
for (let x of range(1, Infinity)) {
if (x > 10) {
break;
}
console.log(x);
}
Did you notice the range(1, Infinity) bit?
In a classic implementation where all the numbers are generated in advance, this code wouldn't even run as you would get stuck in an infinite number-generating loop.

As I understand it, the range method generates an array of numbers starting from start, stepping with step (default = 1) until end. This simple function does just that :-)
const range = (start, end, step = 1) => {
let result = [];
for(let i=0; i<=end; i+=step) {
result.push(i);
}
return result;
};
What is nice here is that you can use decimal step values.
For example:
range(0, 2, 0.5) // [ 0, 0.5, 1, 1.5, 2 ]
range(0,100,20) // [ 0, 20, 40, 60, 80, 100 ]

Related

How to create an array of pseudo-random numbers that are spread out over an interval?

I would like to create an array of n (steps) integers within an interval [a (min), b (max)] which can be done like this (there are probably smarter ways):
function randomFromInterval(min, max, steps) {
return new Array(steps).fill(0).map(n => Math.floor(Math.random() * (max - min + 1) + min));
}
console.log(randomFromInterval(1, 100, 10));
Unfortunately, with truly random integers, it could happen that the result of randomFromInterval(1, 100, 5) is for example [1,2,3,4,5] which I would like to mitigate. The values of randomFromIntervalButSpread(min, max, steps) should therefore be spread out over the interval in such a way that
randomFromIntervalButSpread(0, 4, 5) => [0,1,2,3,4]
randomFromIntervalButSpread(10, 60, 5) => [1X,2X,3X,4X,5X] // X being 0-9
.
.
.
To summarize: The numbers should be random in the sense that if steps > |[min, max]| the results differ from iteration to iteration but are never grouped together within the interval.
Iterate through the steps and generate values with the appropriate floor and ceiling. Note that in this function, end is exclusive.
function randomFromIntervalButSpread(start, end, intervals) {
var arr = [];
var range = (end - start) / intervals;
// Generates a value between start and start + range
// Moves start up to the next step
while (start < end) {
arr.push(Math.floor(Math.random() * range + start));
start += range;
}
return arr;
}
console.log(randomFromIntervalButSpread(0, 5, 5))
console.log(randomFromIntervalButSpread(1, 100, 10))
Short function:
function randomFromIntervalButSpread(min, max, steps) {
return new Array(steps).fill(0).map((n, i) => Math.floor(Math.random()*((max-min)/steps+1)+(i*(max-min))/steps+min));
}
console.log(randomFromIntervalButSpread(0, 5, 5))
console.log(randomFromIntervalButSpread(100, 200, 10))
+ shuffle:
function randomFromIntervalButSpread(min, max, steps) {
return new Array(steps).fill(0).map((n, i) => Math.floor(Math.random()*((max-min)/steps+1)+(i*(max-min))/steps+min)).sort(() => Math.random() - 0.5);
}
console.log(randomFromIntervalButSpread(0, 5, 5))
console.log(randomFromIntervalButSpread(100, 200, 10))

Sized array of random unique numbers

I was wondering what was the most concise way to get an array of a certain size, of unique random numbers.
I get random numbers like this:
times(4, () => random(30, 95));
However this is not unique. I can filter this with uniq but I need to gurantee length of 4 of array. And I want to do it the lodash way. Any ideas?
Much easiear would be...
const uniqRandomNumbers = _.sampleSize(_.range(30, 95), 4);
console.log(uniqRandomNumbers);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
I know this isn't "the lodash way", but it guarantees uniqueness, and allows you to use the same arguments as you were using before. It also scales better than methods that require a binary or linear search through an array, as set.has() is O(1) on average, rather than O(log(n)) or O(n).
function uniqRandom (times, ...args) {
const set = new Set()
while (times > 0) {
const rand = _.random(...args)
if (!set.has(rand)) {
set.add(rand)
times--
}
}
return Array.from(set)
}
console.log(uniqRandom(4, 30, 33));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
I solved it using from a functional programming perspective. refillRandom is a recursive function that checks the number of items left to generate and calls itself again until the are the required number of items.
It also throws an Error when is imposible to generate the sequence, if the distance between min and max random number is greater than the required unique items. It's better to throw an Error than waiting forever.
const generator = (min, offset) => () =>
Math.floor(Math.random() * offset + min);
const refillRandom = (list, min, max, times) => {
const offset = max - min,
num = times - list.length;
if (times > offset) {
throw new Error("Imposible to generate it");
}
const result = _.uniq(_.times(num, generator(min,offset)));
if (result.length < num) {
return result.concat(
refillRandom(list, min, max, num - result.length)
);
}
return result;
}
const r = refillRandom([], 30, 95, 4);
console.log(r);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
EDIT: I found another solution, I mantain an ordered array of generated numbers and increment the generated number so it get mapped to a number that has not been generated yet. This way I only call random the times specified.
const randomGenerator = (min, offset, generated, times) => {
if (!times || !offset) return generated;
var number = Math.floor(Math.random() * offset + min);
const len = generated.length;
for (var i = 0; i < len; i++) {
if (generated[i] <= number) {
number++;
} else {
generated.splice(i, 0, number);
return randomGenerator(min, offset - 1, generated, times - 1);
}
}
generated[i] = number;
return randomGenerator(min, offset - 1, generated, times - 1);
};
const r = randomGenerator(30, 95 - 30, [], 12);
console.log(r);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>

looping through math operators using eval

var toyProblem = function () {
var sol= 0;
var operators = ['+','-','*','/'];
console.log(sol)
for(var i in arguments){
for(var j in operators){
sol = eval(sol + (operators[j]) + arguments[i]);
}
}
return sol;
}
toyProblem(6, 0, 10, 3); //6 + 0 - 10 * 3 === -12)
I'm trying to loop through 4 math operators for an unknown number of input values. I'm thinking of using eval in a nest for loop as a way of going through both the unknown number of arguments while also changing the math operator. At the bottom is the solution that I want to arrive at. Is this a good way of going about this problem or am I barking up the wrong tree?
var toyProblem = function () {
var sol_str='';
var operators = ['+','-','*','/'];
for(var i in operators){
var prev_operator=(i-1);
if(sol_str!=''){sol_str+=operators[prev_operator];}
sol_str +=arguments[i];
}
console.log(sol_str);
return eval(sol_str);
}
console.log(toyProblem(6, 0, 10, 3));
Nesting the 2 loops will result in doing 6 + 6 - 6 * 6 / 6 + 0 - 0 * 0 / 0 + 10 - 10 * 10 / 10 + 3 - 3 * 3 / 3
I didn't find a way to do this without eval as looping through operations one by one would modify the operators priority so this is what I propose : Building an operation 'query' to be eval'd and returned.
Hope this helps
var toyProblem = function () {
var operation = '';
var operators = ['+','-','*','/'];
var args = Array.from(arguments);
args.forEach(function (arg, index) {
if (index > 0) {
operation += operators[index - 1];
}
operation += arg;
});
return eval(operation);
}
console.log(6 + 0 - 10 * 3);
console.log(toyProblem(6, 0, 10, 3)); //6 + 0 - 10 * 3 === -24)
Let's decompose the problem. You have a variadic function that accepts unknown number of arguments and applies an operator to each next argument depending on the index of that element.
Because the number of arguments can be greater than the number of operators, it's appropriate to use modulo operator to infinitely loop through the array of operators while going once through the list of arguments.
The eval operation takes a string, evaluates it, and returns the result of evaluation of the expression that string represents. So you're on the right track. But because eval function takes a string as the first argument, I'd recommend using template literals, it's supported in almost all browsers natively and doesn't need to be transpiled into good old ES5.
The function then would look like this:
function toyProblem(first = 0, ...args) {
const operators = ['+', '-', '*', '/'];
let sol = first;
for (let i in args) {
sol = eval(`${sol} ${operators[i % operators.length]} ${args[i]}`);
}
return sol;
}
However, as there is recommended in the comments, using eval isn't something you'd like to ship to users. Instead, I'd suggest using functions. Functions in Javascript are first-class citizens, so you can pass them as an argument.
Imagine that you have a function (a, b) => a + b instead of just a string "+". The code would then look like this:
function toyProblem(first = 0, ...args) {
const operations = [
(a, b) => a + b,
(a, b) => a - b,
(a, b) => a * b,
(a, b) => a / b,
];
let sol = first;
for (let i in args) {
sol = operations[i](sol, args[i]);
}
return sol;
}
You could go even further and make the function universal in terms of possible operations. Have fun!
Since you are hard coding the names of the operators you might as well hard code the functions and avoid eval. You put the functions into an array that will let you loop through. Then you can just reduce through the arguments with a simple one-liner, which will handle any amount of arguments:
const op = [
(a, b) => a + b,
(a, b) => a - b,
(a, b) => a * b,
(a, b) => a / b
]
function prob(...args){
return args.reduce((curr, acc, idx) => op[(idx - 1) % op.length](curr, acc))
}
console.log(prob(6, 0, 10, 3))
console.log(prob(6, 0, 10, 3, 20, 11, 15, 100))
To get product -12 the first three parts of the expression need to be evaluated within parentheses, else the result will be -24. You can use String.prototype.replace() to replace "," characters after calling .toString() on input array, replace the "," with the operator, return the expression (6 + 0 - 10) * 3 from Function() constructor
var toyProblem = function () {
var operators = ['+','-','*'];
var opts = operators.slice(0);
var n = [...arguments];
var res = n.toString().replace(/,/g, () => opts.shift());
var index = res.indexOf(operators[operators.length -1]);
return new Function(`return (${res.slice(0, index)})${res.slice(index)}`)();
}
var product = toyProblem(6, 0, 10, 3);
console.log(product);

making a range function in javascript

I am trying to make a function in javascript that returns an array from range(start,end) and im supposed to make an optional argument that defaults to one when it is undefined. I can get the function to work when I provide all the arguments but returns an empty array when I only pass two arguments. Here is the question:
Write a range function that takes two arguments, start and end, and returns an array containing all the numbers from start up to (and including) end.
Next, write a sum function that takes an array of numbers and returns the sum of these numbers. Run the previous program and see whether it does indeed return 55.
As a bonus assignment, modify your range function to take an optional third argument that indicates the “step” value used to build up the array. If no step is given, the array elements go up by increments of one, corresponding to the old behavior. The function call range(1, 10, 2) should return [1, 3, 5, 7, 9]. Make sure it also works with negative step values so that range(5, 2, -1) produces [5, 4, 3, 2].
And here is my code:
function range(start, end, increment){
var array = [];
var current = start;
var counter;
if (increment == undefined){
counter = 1;
}
else {
counter = increment;
}
if (increment > 0){
while(current <= end){
array.push(current);
current += counter;
}
}
else if (increment < 0){
while(current >= end){
array.push(current);
current += counter;
}
}
return array;
}
can someone explain why its breaking? I know some c# and I used to being able to jump into the debugger in visual studio when something goes wrong unlike javascript.
A very simple unidirectional (ascending), inclusive range – goes from x to y incrementing by 1 each time.
// range :: (Int, Int) -> [Int]
const range = (x,y) =>
x > y ? [] : [x, ...range(x + 1, y)];
console.log(range(1,4)); // [1,2,3,4]
console.log(range(3,3)); // [3]
console.log(range(6,3)); // []
A slight adaptation that supports bidirectional (ascending or descending) range – still increments or decrements by 1
// range :: (Int, Int) -> [Int]
const range = (x,y) => {
if (x > y)
return range(y,x).reverse();
else
return x === y ? [y] : [x, ...range(x + 1, y)];
}
console.log(range(1,4)); // [1,2,3,4]
console.log(range(3,3)); // [3]
console.log(range(6,3)); // [6,5,4,3]
Another adaptation that uses higher-order functions for more control over the range – this effectively gives you the stepping/incrementing behaviour some of you are looking for – tho this is more powerful because it lets you use a function, t, to choose the next value.
const gte = x => y => y >= x;
const lte = x => y => y <= x;
const add = x => y => y + x;
const sub = x => y => y - x;
// range :: (Int, (Int -> Bool), (Int -> Int)) -> [Int]
const range = (x, p, t) => {
if (p(x))
return [x, ...range(t(x), p, t)];
else
return [];
};
console.log(range(2, lte(8), add(2))); // [2,4,6,8]
console.log(range(9, gte(0), sub(3))); // [9,6,3,0]
console.log(range(9, gte(0), sub(5))); // [9, 4]
// very power. wow.
const double = x => x + x;
console.log(range(2, lte(50), double)); // [2,4,8,16,32]
This function has the same risks inherent with for and while – it's up to you to make sure you don't put it into an infinite loop.
functional overload
Warning: Esoteric, impractical functionals ahead. The following information is provided for your academic pleasure only.
The range function also happens to be one of my favourite demonstrations of the Y combinator. I'll show you two examples here.
naïve range
const U = f => f (f);
const Y = U (h => f => f (x => h (h) (f) (x)));
const range = Y (f => acc => x => y =>
x > y ? acc : f ([...acc, x]) (x + 1) (y)
) ([]);
console.log(range (3) (6)); // [3,4,5,6]
console.log(range (6) (6)); // [6]
console.log(range (9) (6)); // []
and the higher-order range
const U = f => f (f);
const Y = U (h => f => f (x => h (h) (f) (x)));
const lt = x => y => y < x;
const gt = x => y => y > x;
const add1 = x => x + 1;
const sub1 = x => x - 1;
const range = Y (f => acc => x => p => t =>
p(x) ? f ([...acc, x]) (t(x)) (p) (t) : acc
) ([]);
console.log(range (3) (lt(6)) (add1)); // [3,4,5]
console.log(range (6) (lt(6)) (add1)); // []
console.log(range (9) (gt(6)) (sub1)); // [9,8,7]
What a thing of beauty that is.
You could simplify the code a bit and use the increment variable for incrementing. But before, I suggest to test if the value is falsy (0, null, undefined, etc) and assign then 1 to it.
Not implemented: check if start and end is appropriate.
function range(start, end, increment) {
var array = [];
var current = start;
increment = increment || 1;
if (increment > 0) {
while (current <= end) {
array.push(current);
current += increment;
}
} else {
while (current >= end) {
array.push(current);
current += increment;
}
}
return array;
}
console.log(range(1, 3, 0));
console.log(range(2, 5));
console.log(range(1, 9, 1));
console.log(range(5, 2, -1));
First you check if increment is undefined and set counter accordingly, but later you check if (increment > 0){ again. While it is undefined none of your cases matches, so nothing happens.
Change your checks to this:
if (counter > 0){
// ...
}
else if (counter < 0){
// ...
}
Very compact range function that handles float and negative numbers:
const range = (lower,upper,step)=>{
return Array.from(new Array(Math.floor(upper/step-lower/step)+1),(_,i)=>lower/step+i).map(x=>x*step)
}
For example you can use it like:
range(10,30,3) // [10, 13, 16, 19, 22, 25, 28]
range(0,0.5,0.01) // [0, 0.01, 0.02, ... , 0.48, 0.49, 0.5]
range(1,10,2) // [1, 3, 5, 7, 9]
range(-5,10,0.5) // [-5, -4.5, -4, ... , 1, 1.5, 2]
range(5,2,-0.5) // [5, 4.5, 4, 3.5, 3, 2.5, 2]
Here a more understandable version:
const range = (lower, upper, step) => {
end = upper / step // Upper bound
start = lower / step // Lower bound
n = Math.floor(end - start) + 1 // Size that includes upper bound as well
zeros_arr = Array(n).fill(0) // Init array with n zeros
unscaled_arr = zeros_arr.map((_, i) => (start + i)) // Setting each zero to the upper bound + the index
range_arr = unscaled_arr.map(x => (x * step)) // Scaling every numbers with the step
return range_arr
}
The given answers are great. I just wanted to give you an idea of how a more functional approach could solve the task:
// auxiliary functions:
const append = (x, xs) => xs.concat([x]);
const prepend = (x, xs) => [x].concat(xs);
// main function
const range = (x, y, step, acc = [], op = append) =>
step && step < 0
? range(y, x, -step, acc, prepend)
: step && x <= y
? range(x + step, y, step, op(x, acc), op) // tail call
: acc;
console.log(range(1,5,1)); // [1,2,3,4,5]
console.log(range(1,5,2)); // [1,3,5]
console.log(range(1,5,6)); // [1]
console.log(range(5,1,1)); // []
console.log(range(1,5,0)); // []
console.log(range(5,1,-1)); // [5,4,3,2,1]
console.log(range(5,1,-2)); // [5,3,1]
console.log(range(5,1,-6)); // [1]
console.log(range(1,5,-1)); // []
Algorithm:
acc = [] and op = append default parameter values (are taken if omitted during the function invocation)
step && step < 0 short circuits if step is zero, otherwise checks if step is negative
range(y, x, -step, acc, prepend) is called when step is negative and converts range's parameterization so that step can be positive (note that -step is equivalent with -(-1), which is evaluated to 1)
range(x + step, y, step, op(x, acc), op) recursive case that means, the function calls itself (notice that op can be either append or prepend depending on the initial sign of step, that x is increased by step and appended/prepended to acc)
acc base case that stops the recursion and returns the accumulated array
I solved this problem as part of the eloquent javascript course. Based on the problem statement I chose default parameters for the increments and used a while loop to include the second argument.
function range(start, stop, increment=1)
{
let range_arr = [];
if(start < stop)
{
while( start <= stop)
{
range_arr.push(start);
start += increment;
}
}
else{
while( start >= stop)
{
range_arr.push(start);
start += increment;
}
}
return range_arr;
}
I decided to use the for loop to create the increments as follows.
function range(start,end) {
let array = [];
for (let counter = 0; counter < end; counter++) {
array.push(start);
start += 1;
}
return array;
}
console.log(range(1,10)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
I've using the snippet for range, with help of iterative callback theory.
That repeat the same function function with respected range attribute i.e 10-100. also 10-100 with 2 or 5 different.
function range(n, m, callback, k){
if(!k) k=1;
while(true){
if(n<=m){
callback(n);
n+=k;
if(n>=m) break;
}else{
callback(n);
n-=k
if(n<=m) break;
}
}
}
you can execute the snippet by
range(10, 100,function(n){
// do here
},10)
you can extends the snippets by
function range_arr(n, m,callback,k) {
let a = []
range(n,m,function(n){
a.push(callback(n))
},k)
return a;
}
you can use this snippet by
let arr=range_arr(0,10,function(n){
return n=n*n;
},2);
(5)[0, 4, 16, 36, 64]
I realize this is an extremely old question and you probably don't need it anymore, but just in case someone reads this, my favorite way to do this is using a generator:
function* rangeGenerator(start, end = null, step = 1) {
if (end == null) {
end = start
start = 0
}
if (Math.sign(end - start) !== Math.sign(step)) {
step *= -1
}
while (Math.sign(step) === 1 ? start < end : start > end) {
yield start
start += step
}
}
const range = (start, end = null, step = 1) => [...rangeGenerator(start, end, step)]
This way, the range function will generate an array that goes from start (inclusive) to end (exclsive). step should always be provided as a positive number because the sign is automatically handled by the generator according to whiche of start and end is bigger. You can pass it a negative step, but it's unnecessary.
This gives you the ability to use a for of loop over the range
for (let i = 0; i < 100; i++) {}
// Is the same as
for (const i of range(100)) {}

How to compute the sum and average of elements in an array? [duplicate]

Want to improve this post? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. Answers without enough detail may be edited or deleted.
This question already has answers here:
How to find the sum of an array of numbers
(59 answers)
Closed 3 months ago.
I am having problems adding all the elements of an array as well as averaging them out. How would I do this and implement it with the code I currently have? The elements are supposed to be defined as I have it below.
<script type="text/javascript">
//<![CDATA[
var i;
var elmt = new Array();
elmt[0] = "0";
elmt[1] = "1";
elmt[2] = "2";
elmt[3] = "3";
elmt[4] = "4";
elmt[5] = "7";
elmt[6] = "8";
elmt[7] = "9";
elmt[8] = "10";
elmt[9] = "11";
// Problem here
for (i = 9; i < 10; i++){
document.write("The sum of all the elements is: " + /* Problem here */ + " The average of all the elements is: " + /* Problem here */ + "<br/>");
}
//]]>
</script>
A solution I consider more elegant:
const sum = times.reduce((a, b) => a + b, 0);
const avg = (sum / times.length) || 0;
console.log(`The sum is: ${sum}. The average is: ${avg}.`);
ES6
const average = arr => arr.reduce( ( p, c ) => p + c, 0 ) / arr.length;
const result = average( [ 4, 4, 5, 6, 6 ] ); // 5
console.log(result);
var sum = 0;
for( var i = 0; i < elmt.length; i++ ){
sum += parseInt( elmt[i], 10 ); //don't forget to add the base
}
var avg = sum/elmt.length;
document.write( "The sum of all the elements is: " + sum + " The average is: " + avg );
Just iterate through the array, since your values are strings, they have to be converted to an integer first. And average is just the sum of values divided by the number of values.
Calculating average (mean) using reduce and ES6:
const average = list => list.reduce((prev, curr) => prev + curr) / list.length;
const list = [0, 10, 20, 30]
average(list) // 15
Shortest one liner for Average
const avg = arr => arr.reduce((acc,v,i,a)=>(acc+v/a.length),0);
Shortest one liner for Sum
const sum = arr => arr.reduce((a,b)=>a+b);
Let's imagine we have an array of integers like this:
var values = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
The average is obtained with the following formula
A= (1/n)Σxi ( with i = 1 to n ) ... So: x1/n + x2/n + ... + xn/n
We divide the current value by the number of values and add the previous result to the returned value.
The reduce method signature is
reduce(callback[,default_previous_value])
The reduce callback function takes the following parameters:
p : Result
of the previous calculation
c : Current value (from the current index)
i : Current array element's index value
a : The current reduced Array
The second reduce's parameter is the default value ... (Used in case the array is empty ).
So the average reduce method will be:
var avg = values.reduce(function(p,c,i,a){return p + (c/a.length)},0);
If you prefer you can create a separate function
function average(p,c,i,a){return p + (c/a.length)};
function sum(p,c){return p + c)};
And then simply refer to the callback method signature
var avg = values.reduce(average,0);
var sum= values.reduce(sum,0);
Or Augment the Array prototype directly..
Array.prototype.sum = Array.prototype.sum || function (){
return this.reduce(function(p,c){return p+c},0);
};
It's possible to divide the value each time the reduce method is called..
Array.prototype.avg = Array.prototype.avg || function () {
return this.reduce(function(p,c,i,a){return p+(c/a.length)},0);
};
Or even better , using the previously defined Array.protoype.sum()
method, optimize the process my calling the division only once :)
Array.prototype.avg = Array.prototype.avg || function () {
return this.sum()/this.length;
};
Then on any Array object of the scope:
[2, 6].avg();// -> 4
[2, 6].sum();// -> 8
NB: an empty array with return a NaN wish is more correct than 0 in my point of view and can be useful in specific use cases.
generally average using one-liner reduce is like this
elements.reduce(function(sum, a,i,ar) { sum += a; return i==ar.length-1?(ar.length==0?0:sum/ar.length):sum},0);
specifically to question asked
elements.reduce(function(sum, a,i,ar) { sum += parseFloat(a); return i==ar.length-1?(ar.length==0?0:sum/ar.length):sum},0);
an efficient version is like
elements.reduce(function(sum, a) { return sum + a },0)/(elements.length||1);
Understand Javascript Array Reduce in 1 Minute
http://www.airpair.com/javascript/javascript-array-reduce
as gotofritz pointed out seems Array.reduce skips undefined values.
so here is a fix:
(function average(arr){var finalstate=arr.reduce(function(state,a) { state.sum+=a;state.count+=1; return state },{sum:0,count:0}); return finalstate.sum/finalstate.count})([2,,,6])
You can also use lodash, _.sum(array) and _.mean(array) in Math part (also have other convenient stuff).
_.sum([4, 2, 8, 6]);
// => 20
_.mean([4, 2, 8, 6]);
// => 5
Not the fastest, but the shortest and in one line is using map() & reduce():
var average = [7,14,21].map(function(x,i,arr){return x/arr.length}).reduce(function(a,b){return a + b})
I use these methods in my personal library:
Array.prototype.sum = Array.prototype.sum || function() {
return this.reduce(function(sum, a) { return sum + Number(a) }, 0);
}
Array.prototype.average = Array.prototype.average || function() {
return this.sum() / (this.length || 1);
}
EDIT:
To use them, simply ask the array for its sum or average, like:
[1,2,3].sum() // = 6
[1,2,3].average() // = 2
In ES6-ready browsers this polyfill may be helpful.
Math.sum = (...a) => Array.prototype.reduce.call(a,(a,b) => a+b)
Math.avg = (...a) => Math.sum(...a)/a.length;
You can share same call method between Math.sum,Math.avg and Math.max,such as
var maxOne = Math.max(1,2,3,4) // 4;
you can use Math.sum as
var sumNum = Math.sum(1,2,3,4) // 10
or if you have an array to sum up,you can use
var sumNum = Math.sum.apply(null,[1,2,3,4]) // 10
just like
var maxOne = Math.max.apply(null,[1,2,3,4]) // 4
One sneaky way you could do it although it does require the use of (the much hated) eval().
var sum = eval(elmt.join('+')), avg = sum / elmt.length;
document.write("The sum of all the elements is: " + sum + " The average of all the elements is: " + avg + "<br/>");
Just thought I'd post this as one of those 'outside the box' options. You never know, the slyness might grant you (or taketh away) a point.
Here is a quick addition to the “Math” object in javascript to add a “average” command to it!!
Math.average = function(input) {
this.output = 0;
for (this.i = 0; this.i < input.length; this.i++) {
this.output+=Number(input[this.i]);
}
return this.output/input.length;
}
Then i have this addition to the “Math” object for getting the sum!
Math.sum = function(input) {
this.output = 0;
for (this.i = 0; this.i < input.length; this.i++) {
this.output+=Number(input[this.i]);
}
return this.output;
}
So then all you do is
alert(Math.sum([5,5,5])); //alerts “15”
alert(Math.average([10,0,5])); //alerts “5”
And where i put the placeholder array just pass in your variable (The input if they are numbers can be a string because of it parsing to a number!)
I found Mansilla's answer to work fine with the extension of making sure that I am doing summation of floats and not concatonation of strings using parseFloat():
let sum = ourarray.reduce((a, b) => parseFloat(a) + parseFloat(b), 0);
let avg = (sum / ourarray.length) || 0;
console.log(sum); // print out sum
console.log(avg); // print out avg
set your for loop counter to 0.... you're getting element 9 and then you're done as you have it now. The other answers are basic math. Use a variable to store your sum (need to cast the strings to ints), and divide by your array length.
Start by defining all of the variables we plan on using. You'll note that for the numbers array, I'm using the literal notation of [] as opposed to the constructor method array(). Additionally, I'm using a shorter method to set multiple variables to 0.
var numbers = [], count = sum = avg = 0;
Next I'm populating my empty numbers array with the values 0 through 11. This is to get me to your original starting point. Note how I'm pushing onto the array count++. This pushing the current value of count, and then increments it for the next time around.
while ( count < 12 )
numbers.push( count++ );
Lastly, I'm performing a function "for each" of the numbers in the numbers array. This function will handle one number at a time, which I'm identifying as "n" within the function body.
numbers.forEach(function(n){
sum += n;
avg = sum / numbers.length;
});
In the end, we can output both the sum value, and the avg value to our console in order to see the result:
// Sum: 66, Avg: 5.5
console.log( 'Sum: ' + sum + ', Avg: ' + avg );
See it in action online at http://jsbin.com/unukoj/3/edit
I am just building on Abdennour TOUMI's answer. here are the reasons why:
1.) I agree with Brad, I do not think it is a good idea to extend object that we did not create.
2.) array.length is exactly reliable in javascript, I prefer Array.reduce beacuse a=[1,3];a[1000]=5; , now a.length would return 1001.
function getAverage(arry){
// check if array
if(!(Object.prototype.toString.call(arry) === '[object Array]')){
return 0;
}
var sum = 0, count = 0;
sum = arry.reduce(function(previousValue, currentValue, index, array) {
if(isFinite(currentValue)){
count++;
return previousValue+ parseFloat(currentValue);
}
return previousValue;
}, sum);
return count ? sum / count : 0;
};
Array.prototype.avg=function(fn){
fn =fn || function(e,i){return e};
return (this.map(fn).reduce(function(a,b){return parseFloat(a)+parseFloat(b)},0) / this.length ) ;
};
Then :
[ 1 , 2 , 3].avg() ; //-> OUT : 2
[{age:25},{age:26},{age:27}].avg(function(e){return e.age}); // OUT : 26
On evergreen browsers you can use arrow functions
avg = [1,2,3].reduce((a,b) => (a+b);
Running it 100,000 times, the time difference between the for loop approach and reduce is negligible.
s=Date.now();for(i=0;i<100000;i++){ n=[1,2,3]; a=n.reduce((a,b) => (a+b)) / n.length };
console.log("100k reduce took " + (Date.now()-s) + "ms.");
s=Date.now();for(i=0;i<100000;i++){n=[1,2,3]; nl=n.length; a=0; for(j=nl-1;j>0;j--){a=a+n[j];} a/nl };
console.log("100k for loop took " + (Date.now()-s) + "ms.");
s=Date.now();for(i=0;i<1000000;i++){n=[1,2,3]; nl=n.length; a=0; for(j=nl-1;j>0;j--){a=a+n[j];} a/nl };
console.log("1M for loop took " + (Date.now()-s) + "ms.");
s=Date.now();for(i=0;i<1000000;i++){ n=[1,2,3]; a=n.reduce((a,b) => (a+b)) / n.length };
console.log("1M reduce took " + (Date.now()-s) + "ms.");
/*
* RESULT on Chrome 51
* 100k reduce took 26ms.
* 100k for loop took 35ms.
* 10M for loop took 126ms.
* 10M reduce took 209ms.
*/
If you are in need of the average and can skip the requirement of calculating the sum, you can compute the average with a single call of reduce:
// Assumes an array with only values that can be parsed to a Float
var reducer = function(cumulativeAverage, currentValue, currentIndex) {
// 1. multiply average by currentIndex to find cumulative sum of previous elements
// 2. add currentValue to get cumulative sum, including current element
// 3. divide by total number of elements, including current element (zero-based index + 1)
return (cumulativeAverage * currentIndex + parseFloat(currentValue))/(currentIndex + 1)
}
console.log([1, 2, 3, 4, 5, 6, 7, 8, 9, 10].reduce(reducer, 0)); // => 5.5
console.log([].reduce(reducer, 0)); // => 0
console.log([0].reduce(reducer, 0)); // => 0
console.log([].reduce(reducer, 0)); // => 0
console.log([,,,].reduce(reducer, 0)); // => 0
console.log([].reduce(reducer, 0)); // => 0
If anyone ever needs it - Here is a recursive average.
In the context of the original question, you may want to use the recursive average if you allowed the user to insert additional values and, without incurring the cost of visiting each element again, wanted to "update" the existing average.
/**
* Computes the recursive average of an indefinite set
* #param {Iterable<number>} set iterable sequence to average
* #param {number} initAvg initial average value
* #param {number} initCount initial average count
*/
function average(set, initAvg, initCount) {
if (!set || !set[Symbol.iterator])
throw Error("must pass an iterable sequence");
let avg = initAvg || 0;
let avgCnt = initCount || 0;
for (let x of set) {
avgCnt += 1;
avg = avg * ((avgCnt - 1) / avgCnt) + x / avgCnt;
}
return avg; // or {avg: avg, count: avgCnt};
}
average([2, 4, 6]); //returns 4
average([4, 6], 2, 1); //returns 4
average([6], 3, 2); //returns 4
average({
*[Symbol.iterator]() {
yield 2; yield 4; yield 6;
}
}); //returns 4
How:
this works by maintaining the current average and element count. When a new value is to be included you increment count by 1, scale the existing average by (count-1) / count, and add newValue / count to the average.
Benefits:
you don't sum all the elements, which may result in large number that cannot be stored in a 64-bit float.
you can "update" an existing average if additional values become available.
you can perform a rolling average without knowing the sequence length.
Downsides:
incurs lots more divisions
not infinite - limited to Number.MAX_SAFE_INTEGER items unless you employ BigNumber
Having read the other choices, I will try to make a simpler version for the future viewers, elaborating on the existing code and not creating a more elegant one. First of all, you declared the numbers as strings. Apart from the .parseInt we can also do:
const numberConverter = elmt.map(Number);
So what map does is that it "returns a copy of the original array". But I convert its values to numbers. Then we can use the reduce method (It can also be simpler, but I am writing easy to read versions and I also have 2 average methods) What the reduce method does is it has an accumulator that gets bigger and bigger if you add values to it, as it iterates through the array and adds (in this case) the currentValue to it.:
var i;
const elmt = new Array();
elmt[0] = '0';
elmt[1] = '1';
elmt[2] = '2';
elmt[3] = '3';
elmt[4] = '4';
elmt[5] = '7';
elmt[6] = '8';
elmt[7] = '9';
elmt[8] = '10';
elmt[9] = '11';
console.log(elmt);
const numberConverter = elmt.map(Number);
const sum = numberConverter.reduce((accumulator, currentValue) => {
return accumulator + currentValue;
}, 0);
const average = numberConverter.reduce(
(accumulator, currentvalue, index, numArray) => {
return accumulator + currentvalue / numArray.length;
},
0
);
const average2 =
numberConverter.reduce(
(accumulator, currentValue) => accumulator + currentValue,
0
) / numberConverter.length;
for (i = 9; i < 10; i++) {
console.log(
`The sum of all the elements is: ${sum}. <br> The average of all the elements is: ${average2}`
);}
Unless I missed something, every solution up to this point uses the length of the list to calculate the average after summing the values.
There is a downside to this approach that a slightly modified, yet still simple algorithm will address without the downsides.
The downside is that you assuming that there won't be an overflow by summing all the numbers. If you have a lot of numbers that are very big, and you add them all up, they may exceed the maximum size that can fit into the data type.
A better approach is to simply calculate the average as you go, rather than summing it and then dividing with the length at the end:
function getAvg(values) {
return values.reduce((m, x, i) => m + (x - m) / (i + 1), 0)
}
Props to Knuth's "Art of Computer Programming" vol. 2.
just for fun
let avg = [81, 77, -88, 195, 6.8].reduce((a,e,i) => (a*i+e)/(i+1));
console.log(avg)
Just for kicks:
var elmt = [0, 1, 2,3, 4, 7, 8, 9, 10, 11], l = elmt.length, i = -1, sum = 0;
for (; ++i < l; sum += elmt[i])
;
document.body.appendChild(document.createTextNode('The sum of all the elements is: ' + sum + ' The average of all the elements is: ' + (sum / l)));
I think we can do like
var k=elmt.reduce(function(a,b){return parseFloat(a+parseFloat(b));})
var avg=k/elmt.length;
console.log(avg);
I am using parseFloat twice because
when
1) you add (a)9+b("1") number then result will be "91" but we want addition. so i used parseFloat
2)When addition of (a)9+parseFloat("1") happen though result will be "10" but it will be in string which we don't want so again i used parseFloat.
I hope i am clear. Suggestions are welcome
Here is my rookie way of simply finding the avg. Hope this helps somebody.
function numAvg(num){
var total = 0;
for(var i = 0;i < num.length; i++) {
total+=num[i];
}
return total/num.length;
}
here's your one liner:
var average = arr.reduce((sum,item,index,arr)=>index !== arr.length-1?sum+item:sum+item/arr.length,0)
I think this may be a direct solution to calculate the average with a for loop and function.
var elmts = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
function average(arr) {
var total = 0;
for (var i = 0; i < arr.length; i++) {
total += arr[i];
}
console.log(Math.round(total/arr.length));
}
average(elmts);
There seem to be an endless number of solutions for this but I found this to be concise and elegant.
const numbers = [1,2,3,4];
const count = numbers.length;
const reducer = (adder, value) => (adder + value);
const average = numbers.map(x => x/count).reduce(reducer);
console.log(average); // 2.5
Or more consisely:
const numbers = [1,2,3,4];
const average = numbers.map(x => x/numbers.length).reduce((adder, value) => (adder + value));
console.log(average); // 2.5
Depending on your browser you may need to do explicit function calls because arrow functions are not supported:
const r = function (adder, value) {
return adder + value;
};
const m = function (x) {
return x/count;
};
const average = numbers.map(m).reduce(r);
console.log(average); // 2.5
Or:
const average1 = numbers
.map(function (x) {
return x/count;
})
.reduce(function (adder, value) {
return adder + value;
});
console.log(average1);

Categories