I'm trying to work on this problem where we compute Nested weight sum for a given array of numbers.
Given a nested list of integers, return the sum of all integers in the
list weighted by their depth.
For example for:
[[1,1],2,[1,1]] ====> solution is 10.
Four 1's at depth 2, one 2 at depth 1.
Here's the code i wrote:
var depthSum = function (nestedList, sum=0, depth=1) {
for(let i=0; i<nestedList.length; i++){
let val = nestedList[i];
if (Array.isArray(val)) {
return depthSum(val, sum, depth+1);
} else {
sum += val * depth;
}
};
return sum;
};
I'm trying to work on the converse problem. i.e
Given a nested list of integers, return the sum of all integers in the
list weighted by their depth. Where weight is increasing from root to
leaf, now the weight is defined from bottom up. i.e., the leaf level
integers have weight 1, and the root level integers have the largest
weight.
Example:
[[1,1],2,[1,1]] ===> Solution is 8.
How can I use the same approach and solve this problem?
(https://leetcode.com/problems/nested-list-weight-sum-ii/description/)
This should do the job, but I wish I had a premium leetcode account to verify that. The idea is to do a search to find the maximum depth in the structure, then use your previous algorithm but with the depth calculation inverted. Also, doing it without recursion means less chance of timing out and no chance of blowing the stack. I added a few basic test cases, but again, no guarantees.
const search = a => {
let sum = 0;
let depth = 0;
const stack = [[a, 0]];
while (stack.length) {
const curr = stack.pop();
if (curr[1] > depth) {
depth = curr[1];
}
for (const e of curr[0]) {
if (Array.isArray(e)) {
stack.push([e, curr[1] + 1]);
}
}
}
stack.push([a, ++depth]);
while (stack.length) {
const curr = stack.pop();
for (const e of curr[0]) {
if (Array.isArray(e)) {
stack.push([e, curr[1] - 1]);
}
else {
sum += e * curr[1];
}
}
}
return sum;
};
console.log(search([[1,1],2,[1,1]]));
console.log(search([]));
console.log(search([6]));
console.log(search([[[[3]]]]));
console.log(search([[2],1]));
A basic recursive solution alone like your original depthSum probably won't work for the second requirement, because you need to figure out the total depth before you know the multiplier for the items on the top level of the array. One option is to figure out the depth of the deepest array first, and then use something similar to your original depthSum.
You can use reduce (which is the appropriate method to use to convert an object into a single value) and the conditional (ternary) operator to make your code concise and less repetitive:
const depthCheck = (item) => (
Array.isArray(item)
? 1 + Math.max(...item.map(depthCheck))
: 0
);
// verification:
console.log(depthCheck([[1,1],2,[1,1]])); // total depth 2
console.log(depthCheck([[1,1],2,[1,1,[2,2]]])) // total depth 3
console.log(depthCheck([[1,1,[2,[3,3]]],2,[1,1,[2,2]]])) // total depth 4
console.log('-----')
const depthSum = (nestedList, weight=depthCheck(nestedList)) => (
nestedList.reduce((a, val) => a + (
Array.isArray(val)
? depthSum(val, weight - 1)
: val * weight
), 0)
);
console.log(depthSum([[1,1],2,[1,1]])) // (2)*2 + (1+1+1+1)*1
console.log(depthSum([[1,1],2,[1,1,[2,2]]])) // (2)*3 + (1+1+1+1)*2 + (2+2)*1
console.log(depthSum([[1,1,[2,[3,3]]],2,[1,1,[2,2]]])) // (2)*4 + (1+1+1+1)*3 + (2)*2 + (3+3)*1
You can do this without needing two traversals of the nested array, if you store the sums of the elements per depth in an array during the traversal. Afterwards, you know that the length of this array is the maximum depth, and you can multiply the sums by their correct weight.
The traversal can be done using recursion or a stack, as explained in the other answers. Here's an example using recursion:
function weightedSum(array) {
var sums = [], total = 0;
traverse(array, 0);
for (var i in sums)
total += sums[i] * (sums.length - i);
return total;
function traverse(array, depth) {
if (sums[depth] === undefined)
sums[depth] = 0;
for (var i in array) {
if (typeof array[i] === "number")
sums[depth] += array[i];
else traverse(array[i], depth + 1);
}
}
}
console.log(weightedSum([[],[]]));
console.log(weightedSum([[1,1],2,[1,1]]));
console.log(weightedSum([1,[[],2,2],1,[[3,3,[[5]]],[3]],[]]));
May be you can do as follows with a simple recursive reducer.
var weightOfNested = (a,d=1) => a.reduce((w,e) => Array.isArray(e) ? w + weightOfNested(e,d+1)
: w + d*e, 0);
console.log(weightOfNested([[1,1,[3]],2,[1,1]]));
So OK as mentioned in the comment the above code is weighing the deeper elements more. In order to weigh the shallow ones more we need to know the depth of the array in advance. I believe this way or that way you end up traversing the array twice... once for the depth and once for calculating the weighted sum.
var weightOfNested = (a, d = getDepth(a)) => a.reduce((w,e) => Array.isArray(e) ? w + weightOfNested(e,d-1)
: w + d*e, 0),
getDepth = (a, d = 1, t = 1) => a.reduce((r,e) => Array.isArray(e) ? r === t ? getDepth(e,++r,t+1)
: getDepth(e,r,t+1)
: r, d);
console.log(weightOfNested([[1,1,[3]],2,[1,1]])); // depth is 3
Related
I have a sorted JavaScript array, and want to insert one more item into the array such the resulting array remains sorted. I could certainly implement a simple quicksort-style insertion function:
var array = [1,2,3,4,5,6,7,8,9];
var element = 3.5;
function insert(element, array) {
array.splice(locationOf(element, array) + 1, 0, element);
return array;
}
function locationOf(element, array, start, end) {
start = start || 0;
end = end || array.length;
var pivot = parseInt(start + (end - start) / 2, 10);
if (end-start <= 1 || array[pivot] === element) return pivot;
if (array[pivot] < element) {
return locationOf(element, array, pivot, end);
} else {
return locationOf(element, array, start, pivot);
}
}
console.log(insert(element, array));
[WARNING] this code has a bug when trying to insert to the beginning of the array, e.g. insert(2, [3, 7 ,9]) produces incorrect [ 3, 2, 7, 9 ].
However, I noticed that implementations of the Array.sort function might potentially do this for me, and natively:
var array = [1,2,3,4,5,6,7,8,9];
var element = 3.5;
function insert(element, array) {
array.push(element);
array.sort(function(a, b) {
return a - b;
});
return array;
}
console.log(insert(element, array));
Is there a good reason to choose the first implementation over the second?
Edit: Note that for the general case, an O(log(n)) insertion (as implemented in the first example) will be faster than a generic sorting algorithm; however this is not necessarily the case for JavaScript in particular. Note that:
Best case for several insertion algorithms is O(n), which is still significantly different from O(log(n)), but not quite as bad as O(n log(n)) as mentioned below. It would come down to the particular sorting algorithm used (see Javascript Array.sort implementation?)
The sort method in JavaScript is a native function, so potentially realizing huge benefits -- O(log(n)) with a huge coefficient can still be much worse than O(n) for reasonably sized data sets.
Simple (Demo):
function sortedIndex(array, value) {
var low = 0,
high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
if (array[mid] < value) low = mid + 1;
else high = mid;
}
return low;
}
Just as a single data point, for kicks I tested this out inserting 1000 random elements into an array of 100,000 pre-sorted numbers using the two methods using Chrome on Windows 7:
First Method:
~54 milliseconds
Second Method:
~57 seconds
So, at least on this setup, the native method doesn't make up for it. This is true even for small data sets, inserting 100 elements into an array of 1000:
First Method:
1 milliseconds
Second Method:
34 milliseconds
Very good and remarkable question with a very interesting discussion! I also was using the Array.sort() function after pushing a single element in an array with some thousands of objects.
I had to extend your locationOf function for my purpose because of having complex objects and therefore the need for a compare function like in Array.sort():
function locationOf(element, array, comparer, start, end) {
if (array.length === 0)
return -1;
start = start || 0;
end = end || array.length;
var pivot = (start + end) >> 1; // should be faster than dividing by 2
var c = comparer(element, array[pivot]);
if (end - start <= 1) return c == -1 ? pivot - 1 : pivot;
switch (c) {
case -1: return locationOf(element, array, comparer, start, pivot);
case 0: return pivot;
case 1: return locationOf(element, array, comparer, pivot, end);
};
};
// sample for objects like {lastName: 'Miller', ...}
var patientCompare = function (a, b) {
if (a.lastName < b.lastName) return -1;
if (a.lastName > b.lastName) return 1;
return 0;
};
There's a bug in your code. It should read:
function locationOf(element, array, start, end) {
start = start || 0;
end = end || array.length;
var pivot = parseInt(start + (end - start) / 2, 10);
if (array[pivot] === element) return pivot;
if (end - start <= 1)
return array[pivot] > element ? pivot - 1 : pivot;
if (array[pivot] < element) {
return locationOf(element, array, pivot, end);
} else {
return locationOf(element, array, start, pivot);
}
}
Without this fix the code will never be able to insert an element at the beginning of the array.
I know this is an old question that has an answer already, and there are a number of other decent answers. I see some answers that propose that you can solve this problem by looking up the correct insertion index in O(log n) - you can, but you can't insert in that time, because the array needs to be partially copied out to make space.
Bottom line: If you really need O(log n) inserts and deletes into a sorted array, you need a different data structure - not an array. You should use a B-Tree. The performance gains you will get from using a B-Tree for a large data set, will dwarf any of the improvements offered here.
If you must use an array. I offer the following code, based on insertion sort, which works, if and only if the array is already sorted. This is useful for the case when you need to resort after every insert:
function addAndSort(arr, val) {
arr.push(val);
for (i = arr.length - 1; i > 0 && arr[i] < arr[i-1]; i--) {
var tmp = arr[i];
arr[i] = arr[i-1];
arr[i-1] = tmp;
}
return arr;
}
It should operate in O(n), which I think is the best you can do. Would be nicer if js supported multiple assignment.
here's an example to play with:
Update:
this might be faster:
function addAndSort2(arr, val) {
arr.push(val);
i = arr.length - 1;
item = arr[i];
while (i > 0 && item < arr[i-1]) {
arr[i] = arr[i-1];
i -= 1;
}
arr[i] = item;
return arr;
}
Update 2
#terrymorse pointed out in the comments that javascripts Array.splice method is crazy fast, and it's more than just constant improvement in the time complexity. It seems some linked list magic is being used. It means you still do need a different data structure than a plain array - just that javascript arrays might provide that different data structure natively.
Updated JS Bin link
Your insertion function assumes that the given array is sorted, it searches directly for the location where the new element can be inserted, usually by just looking at a few of the elements in the array.
The general sort function of an array can't take these shortcuts. Obviously it at least has to inspect all elements in the array to see if they are already correctly ordered. This fact alone makes the general sort slower than the insertion function.
A generic sort algorithm is usually on average O(n ⋅ log(n)) and depending on the implementation it might actually be the worst case if the array is already sorted, leading to complexities of O(n2). Directly searching for the insertion position instead has just a complexity of O(log(n)), so it will always be much faster.
Here's a version that uses lodash.
const _ = require('lodash');
sortedArr.splice(_.sortedIndex(sortedArr,valueToInsert) ,0,valueToInsert);
note: sortedIndex does a binary search.
For a small number of items, the difference is pretty trivial. However, if you're inserting a lot of items, or working with a very large array, calling .sort() after each insertion will cause a tremendous amount of overhead.
I ended up writing a pretty slick binary search/insert function for this exact purpose, so I thought I'd share it. Since it uses a while loop instead of recursion, there is no overheard for extra function calls, so I think the performance will be even better than either of the originally posted methods. And it emulates the default Array.sort() comparator by default, but accepts a custom comparator function if desired.
function insertSorted(arr, item, comparator) {
if (comparator == null) {
// emulate the default Array.sort() comparator
comparator = function(a, b) {
if (typeof a !== 'string') a = String(a);
if (typeof b !== 'string') b = String(b);
return (a > b ? 1 : (a < b ? -1 : 0));
};
}
// get the index we need to insert the item at
var min = 0;
var max = arr.length;
var index = Math.floor((min + max) / 2);
while (max > min) {
if (comparator(item, arr[index]) < 0) {
max = index;
} else {
min = index + 1;
}
index = Math.floor((min + max) / 2);
}
// insert the item
arr.splice(index, 0, item);
};
If you're open to using other libraries, lodash provides sortedIndex and sortedLastIndex functions, which could be used in place of the while loop. The two potential downsides are 1) performance isn't as good as my method (thought I'm not sure how much worse it is) and 2) it does not accept a custom comparator function, only a method for getting the value to compare (using the default comparator, I assume).
Here are a few thoughts:
Firstly, if you're genuinely concerned about the runtime of your code, be sure to know what happens when you call the built-in functions! I don't know up from down in javascript, but a quick google of the splice function returned this, which seems to indicate that you're creating a whole new array each call! I don't know if it actually matters, but it is certainly related to efficiency. I see that Breton, in the comments, has already pointed this out, but it certainly holds for whatever array-manipulating function you choose.
Anyways, onto actually solving the problem.
When I read that you wanted to sort, my first thought is to use insertion sort!. It is handy because it runs in linear time on sorted, or nearly-sorted lists. As your arrays will have only 1 element out of order, that counts as nearly-sorted (except for, well, arrays of size 2 or 3 or whatever, but at that point, c'mon). Now, implementing the sort isn't too too bad, but it is a hassle you may not want to deal with, and again, I don't know a thing about javascript and if it will be easy or hard or whatnot. This removes the need for your lookup function, and you just push (as Breton suggested).
Secondly, your "quicksort-esque" lookup function seems to be a binary search algorithm! It is a very nice algorithm, intuitive and fast, but with one catch: it is notoriously difficult to implement correctly. I won't dare say if yours is correct or not (I hope it is, of course! :)), but be wary if you want to use it.
Anyways, summary: using "push" with insertion sort will work in linear time (assuming the rest of the array is sorted), and avoid any messy binary search algorithm requirements. I don't know if this is the best way (underlying implementation of arrays, maybe a crazy built-in function does it better, who knows), but it seems reasonable to me. :)
- Agor.
Here's a comparison of four different algorithms for accomplishing this:
https://jsperf.com/sorted-array-insert-comparison/1
Algorithms
Naive: just push and sort() afterwards
Linear: iterate over array and insert where appropriate
Binary Search: taken from https://stackoverflow.com/a/20352387/154329
"Quick Sort Like": the refined solution from syntheticzero (https://stackoverflow.com/a/18341744/154329)
Naive is always horrible. It seems for small array sizes, the other three dont differ too much, but for larger arrays, the last 2 outperform the simple linear approach.
The best data structure I can think of is an indexed skip list which maintains the insertion properties of linked lists with a hierarchy structure that enables log time operations. On average, search, insertion, and random access lookups can be done in O(log n) time.
An order statistic tree enables log time indexing with a rank function.
If you do not need random access but you need O(log n) insertion and searching for keys, you can ditch the array structure and use any kind of binary search tree.
None of the answers that use array.splice() are efficient at all since that is on average O(n) time. What's the time complexity of array.splice() in Google Chrome?
Here is my function, uses binary search to find item and then inserts appropriately:
function binaryInsert(val, arr){
let mid,
len=arr.length,
start=0,
end=len-1;
while(start <= end){
mid = Math.floor((end + start)/2);
if(val <= arr[mid]){
if(val >= arr[mid-1]){
arr.splice(mid,0,val);
break;
}
end = mid-1;
}else{
if(val <= arr[mid+1]){
arr.splice(mid+1,0,val);
break;
}
start = mid+1;
}
}
return arr;
}
console.log(binaryInsert(16, [
5, 6, 14, 19, 23, 44,
35, 51, 86, 68, 63, 71,
87, 117
]));
Don't re-sort after every item, its overkill..
If there is only one item to insert, you can find the location to insert using binary search. Then use memcpy or similar to bulk copy the remaining items to make space for the inserted one. The binary search is O(log n), and the copy is O(n), giving O(n + log n) total. Using the methods above, you are doing a re-sort after every insertion, which is O(n log n).
Does it matter? Lets say you are randomly inserting k elements, where k = 1000. The sorted list is 5000 items.
Binary search + Move = k*(n + log n) = 1000*(5000 + 12) = 5,000,012 = ~5 million ops
Re-sort on each = k*(n log n) = ~60 million ops
If the k items to insert arrive whenever, then you must do search+move. However, if you are given a list of k items to insert into a sorted array - ahead of time - then you can do even better. Sort the k items, separately from the already sorted n array. Then do a scan sort, in which you move down both sorted arrays simultaneously, merging one into the other.
- One-step Merge sort = k log k + n = 9965 + 5000 = ~15,000 ops
Update: Regarding your question.
First method = binary search+move = O(n + log n). Second method = re-sort = O(n log n) Exactly explains the timings you're getting.
TypeScript version with custom compare method:
const { compare } = new Intl.Collator(undefined, {
numeric: true,
sensitivity: "base"
});
const insert = (items: string[], item: string) => {
let low = 0;
let high = items.length;
while (low < high) {
const mid = (low + high) >> 1;
compare(items[mid], item) > 0
? (high = mid)
: (low = mid + 1);
}
items.splice(low, 0, item);
};
Use:
const items = [];
insert(items, "item 12");
insert(items, "item 1");
insert(items, "item 2");
insert(items, "item 22");
console.log(items);
// ["item 1", "item 2", "item 12", "item 22"]
Had your first code been bug free, my best guess is, it would have been how you do this job in JS. I mean;
Make a binary search to find the index of insertion
Use splice to perform your insertion.
This is almost always 2x faster than a top down or bottom up linear search and insert as mentioned in domoarigato's answer which i liked very much and took it as a basis to my benchmark and finally push and sort.
Of course under many cases you are probably doing this job on some objects in real life and here i have generated a benchmark test for these three cases for an array of size 100000 holding some objects. Feel free to play with it.
function insertElementToSorted(arr, ele, start=0,end=null) {
var n , mid
if (end == null) {
end = arr.length-1;
}
n = end - start
if (n%2 == 0) {
mid = start + n/2;
} else {
mid = start + (n-1)/2
}
if (start == end) {
return start
}
if (arr[0] > ele ) return 0;
if (arr[end] < ele) return end+2;
if (arr[mid] >= ele && arr[mid-1] <= ele) {
return mid
}
if (arr[mid] > ele && arr[mid-1] > ele) {
return insertElementToSorted(arr,ele,start,mid-1)
}
if (arr[mid] <= ele && arr[mid+1] >= ele) {
return mid + 1
}
if (arr[mid] < ele && arr[mid-1] < ele) {
return insertElementToSorted(arr,ele,mid,end)
}
if(arr[mid] < ele && arr[mid+1] < ele) {
console.log("mid+1", mid+1, end)
return insertElementToSorted(arr,ele,mid+1,end)
}
}
// Example
var test = [1,2,5,9, 10, 14, 17,21, 35, 38,54, 78, 89,102];
insertElementToSorted(test,6)
As a memo to my future self, here is yet another version, findOrAddSorted with some optimizations for corner cases and a rudimentary test.
// returns BigInt(index) if the item has been found
// or BigInt(index) + BigInt(MAX_SAFE_INTEGER) if it has been inserted
function findOrAddSorted(items, newItem) {
let from = 0;
let to = items.length;
let item;
// check if the array is empty
if (to === 0) {
items.push(newItem);
return BigInt(Number.MAX_SAFE_INTEGER);
}
// compare with the first item
item = items[0];
if (newItem === item) {
return 0;
}
if (newItem < item) {
items.splice(0, 0, newItem);
return BigInt(Number.MAX_SAFE_INTEGER);
}
// compare with the last item
item = items[to-1];
if (newItem === item) {
return BigInt(to-1);
}
if (newItem > item) {
items.push(newItem);
return BigInt(to) + BigInt(Number.MAX_SAFE_INTEGER);
}
// binary search
let where;
for (;;) {
where = (from + to) >> 1;
if (from >= to) {
break;
}
item = items[where];
if (item === newItem) {
return BigInt(where);
}
if (item < newItem) {
from = where + 1;
}
else {
to = where;
}
}
// insert newItem
items.splice(where, 0, newItem);
return BigInt(where) + BigInt(Number.MAX_SAFE_INTEGER);
}
// generate a random integer < MAX_SAFE_INTEGER
const generateRandomInt = () => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
// fill the array with random numbers
const items = new Array();
const amount = 1000;
let i = 0;
let where = 0;
for (i = 0; i < amount; i++) {
where = findOrAddSorted(items, generateRandomInt());
if (where < BigInt(Number.MAX_SAFE_INTEGER)) {
break;
}
}
if (where < BigInt(Number.MAX_SAFE_INTEGER)) {
console.log(`items: ${i}, repeated at ${where}: ${items[Number(where)]}`)
}
else {
const at = Number(where - BigInt(Number.MAX_SAFE_INTEGER));
console.log(`items: ${i}, last insert at: ${at}: ${items[at]}`);
}
console.log(items);
function insertOrdered(array, elem) {
let _array = array;
let i = 0;
while ( i < array.length && array[i] < elem ) {i ++};
_array.splice(i, 0, elem);
return _array;
}
How to get all possible variations of terms to add up to sum, in a given amount of piles, using javascript?
Let's say I have a sum of 10 and I want to split this into 4 piles with positive terms and zeros only.
function getCombinations(sum, piles){
...
}
getCombinations(10,4);
Returns something like this in a two dimensional array:
[
[3,3,3,1],
[3,3,1,3],
[7,1,1,1],
[10,0,0,0],
...
]
It's not mandatory to return [3,3,3,1] and [3,3,1,3] as different solutions, fastest way will do. I will only work with small numbers, max sum will probably be 10.
It's a variation of the Count the coins problem, http://rosettacode.org/wiki/Count_the_coins, but I want the solutions returned, I have a given set of piles and I use all positive terms (and zero) not only specific coin values.
this should do the trick:
const matrix = (num, cols) => {
const matrix = [[num, ...[...Array(cols-1)].map(() => 0)]];
const hashes = new Set;
const coef = Math.pow(10, cols-1);
let digits = 10 * coef - 1;
while (digits-- >= coef) {
const nums = ('' + digits).split('').map(d => +d);
const hash = nums.sort((a, b) => b - a).join('');
if (hashes.has(hash) || nums.reduce((a, b) => a + b, 0) !== num)
continue;
hashes.add(hash);
matrix.push(nums);
}
return matrix;
};
console.log(matrix(10, 4));
Here is a pratical way of doing this:
function getCombinations(sum, piles){
var array =[];
for(var i = 1; i <= piles; i ++) {
var subArray = Array.apply(null, Array(4)).map(
function () {
return Math.floor(Math.random() * sum)});
array.push(subArray);
}
return array;
}
var result = getCombinations(10,4);
I am pretty new to javascript and working on problems from leetcode.
The description included is:
"Given an array of unique integers salary where salary[i] is the salary of the employee i.
Return the average salary of employees excluding the minimum and maximum salary."
When I run my code it says that I have the incorrect output when the following array is passed in.
[25000,48000,57000,86000,33000,10000,42000,3000,54000,29000,79000,40000]
Expected Output: 41700.00000
My Output: 41000.00000
I've compared my code to other submissions and as far as I can tell mine should run the same.
Here is my code:
function average(salary) {
var sortedSalary = salary.sort();
var total = sortedSalary.reduce((curr, acc) => {
return curr + acc
}, 0);
var result = (total - sortedSalary[0] - sortedSalary[sortedSalary.length - 1]) / (sortedSalary.length - 2);
return result;
};
console.log(average([25000,48000,57000,86000,33000,10000,42000,3000,54000,29000,79000,40000]));
Thank you for any insight into this.
You're sorting the array alphabetically, not by the smallest number, so you end up removing the wrong numbers.
Use sort((a, b) => a - b) instead:
const salary = [25000,48000,57000,86000,33000,10000,42000,3000,54000,29000,79000,40000]
function average(salary) {
var sortedSalary = salary.sort((a, b) => a - b);
var total = sortedSalary.reduce((curr, acc) => { return curr + acc }, 0);
var result = (total - sortedSalary[0] - sortedSalary[sortedSalary.length - 1]) / (sortedSalary.length - 2);
return result;
};
console.log(average(salary))
const average = salary => salary
.sort((a,b) => a - b) // sort numerically
.filter((_,i,l) => i > 0 && i < l.length - 1) // remove first and last index
.reduce((a,s) => a + s) / (salary.length - 2) // calculate average
console.log(average([25000,48000,57000,86000,33000,10000,42000,3000,54000,29000,79000,40000]))
It does not need to be sorted at all. Apart from that, sorting is O(N Log N) not sufficiently efficient here. You just have to come up with an O(N) algorithm for this problem.
This'll pass:
var average = function(salary) {
if (salary.length < 3) {
return 0
}
let min = salary[0]
let max = salary[0]
let sum = 0
for (let sal of salary) {
if (sal > max) {
max = sal
}
if (sal < min) {
min = sal
}
sum += sal
}
return (sum - max - min) / (salary.length - 2)
};
References
For additional details, you can see the Discussion Board. There are plenty of accepted solutions with a variety of languages and explanations, efficient algorithms, as well as asymptotic time/space complexity analysis1, 2 in there.
For interviews:
We'd like to write bug-free and clean codes based on standards and conventions (e.g., c1, 2, c++1, 2, java1, 2, c#1, 2, python1, javascript1, go1, rust1).
For easy questions, brute force algorithms usually get accepted. For interviews, brute force is less desired, especially if the question would be an easy level, like the one you're solving.
I have to write the following weighted average formula in JavaScript:
Average = (p1*p2*x1 + p3*p4*x2 + ... +p(n-2)*p(n-1)*xn) / (p1*p2 + p3*p4 + ... + p(n-2)p(n-1) )
The formula gives the average of x values.
I also have an array populated with n elements in JavaScript:
Array = (p1,p2,x1,p3,p4,x2....)
...where pi are the weights and xi the values I want to find the average for.
How can I write the formula using this array?
I would probably use the following strategy:
Create two new arrays (probably weights and values).
Iterate over the original array in steps of 3; multiplying the pn's and pushing the result into weights and pushing the xn into values.
Iterate over the new arrays, creating the weighted total (the left hand of the division) and the total weight (right hand of the division).
Divide one by the other. Done.
In other words, something like this:
function weighted_average(input) {
var weights = [];
var values = [];
var weighted_total = 0;
var total_weight = 0;;
if (input.length % 3 !== 0) {
throw new Error("Input array length is not a multiple of 3.");
}
for (var i = 0; i < input.length; i += 3) {
weights.push(input[i] * input[i + 1]);
values.push(input[i + 2]);
}
for (var i = 0; i < weights.length; i += 1) {
weighted_total += weights[i] * values[i];
total_weight += weights[i];
}
return weighted_total / total_weight;
}
You'll have to verify whether this does exactly what you're after, though. No guarantees. ;)
JSFiddle demo: jsfiddle.net/Z8seZ
Of course, you could skip the intermediary arrays to make it a bit faster. But the above is more explicit and more readable, and therefore more maintainable (e.g. you could easily split out the actual algorithm and create different "wrapper" functions for different forms of input). I would only optimize it if working with (really) large data sets.
Here's a functional approach, requiring ES5:
var w = a.unzip(3).map(function(v, i, a) {
var weight = v[0] * v[1];
var sum = weight * v[2];
return [sum, weight];
}).reduce(function(p, c, i, a) {
return [p[0] + c[0], p[1] + c[1]];
}, [0, 0]);
var aw = w[0] / w[1];
which in pseudo-code is:
split the array into chunks of three
convert each three [p1, p2, x ] into a pair [ p1 * p2 * x , p1 * p2 ]
sum the pairs (along the array, not within each pair)
divide one by the other
and where the (non-standard) unzip function which chunks the array is:
Object.defineProperty(Array.prototype, 'unzip', {
value: function(n) {
n = n || 2;
return this.reduce(function(p, c, i, a) {
if (i % n === 0) {
p.push(a.slice(i, i + n));
}
return p;
}, []);
}
});
ES6 one-liner for an array of objects xs containing the keys w as weight and v as value:
((_w, _v) => _v / _w)(...xs.reduce((r, o) => [r[0] + o[w], r[1] + o[w] * o[v]], [0, 0]))
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);