-
-
Save caseyjustus/1166258 to your computer and use it in GitHub Desktop.
function median(values) { | |
values.sort( function(a,b) {return a - b;} ); | |
var half = Math.floor(values.length/2); | |
if(values.length % 2) | |
return values[half]; | |
else | |
return (values[half-1] + values[half]) / 2.0; | |
} | |
var list1 = [3, 8, 9, 1, 5, 7, 9, 21]; | |
median(list1); |
Nice Catch!
Thanks. Here it is in CoffeeScript:
getMedian = (values)=> values.sort (a,b)=> return a - b half = Math.floor values.length/2 if values.length % 2 return values[half] else return (values[half-1] + values[half]) / 2.0
I'd a check that values is actually an Array before applying sort(), returning NaN
if it is not...
See http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/ for implementation.
What's:
values.sort( function(a,b) {return a - b;} );
.sort(function(a,b){return a - b;});
is a function on array that will order the items in the array from lowest value to highest.
I don't understand why the if statement is as it is.
if(values.length % 2)
Is this saying "if the division of length by 2 does have a remainder, then do ---"?
I am confused because I was expecting something closer to:
if(values.length % 2 = Something)
but simply if something can be expressed via a modulus seems obvious, so clearly I am missing something.
@nchlswtsn The number 0 is evaluated as false in JavaScript. Any other number is evaluated as true. The code if(values.length % 2)
is essentially the same thing as if(values.length % 2 != 0)
I have added an example of how JS evaluates numbers below.
var i = 0;
if (i) {
console.log('does not print');
}
i = 1;
if (i) {
console.log('does print');
}
i = -1;
if (i) {
console.log('also prints');
}
It should be noted that the approach above changes the input array - it is now sorted for the caller too!
That may be fine in your application, but it's a side effect that I wasn't expecting.
@devison: that's more a symptom of Javascript's seemingly inconsistent parameter passing moreso than the code here. When dealing with primitive types (string, int etc.), the parameter is passed by value i.e. you can do what you like to it in the function without changing that variable in the calling scope. Anything more complex and it sends it by reference which means that it's pretty much a pointer. Any changes will affect the calling scope.
To get around this you could create a copy of the array within the function using slice i.e.
var clone = values.slice( 0 );
Once you've done that, operate on the clone i.e.
clone.sort( function(a,b) {return a - b;} );
One part should be modified.: the if condition part
If the length of array is even
, you should average between (values[half-1]
& values[half]
and if the length if array is odd
, you should take take the values[half]
👍 to @alireza-saberi's point:
function getMedian(args) {
if (!args.length) {return 0};
var numbers = args.slice(0).sort((a,b) => a - b);
var middle = Math.floor(numbers.length / 2);
var isEven = numbers.length % 2 === 0;
return isEven ? (numbers[middle] + numbers[middle - 1]) / 2 : numbers[middle];
}
@ MichalPaszkiewicz Hello what is the use of the function(a,b) {return a - b;} it will sort it anyhow even if list1.sort().
@impari Unfortunately in javascript this isn't the case. You can test this using the following:
[10, 5].sort()
@alireza-saberi's fix along with a second function for already sorted arrays. The median should be just as fast to calculate for massive arrays as little ones, provided the input is already sorted.
function median(values) {
values = values.slice(0).sort( function(a, b) {return a - b; } );
return middle(values);
}
function middle(values) {
var len = values.length;
var half = Math.floor(len / 2);
if(len % 2)
return (values[half - 1] + values[half]) / 2.0;
else
return values[half];
}
var list1 = [3, 8, 9, 1, 5, 7, 9, 21];
median(list1);
list1.sort(function(a, b) {return a - b; });
middle(list1);
@willstott101 I believe your if-statement is wrong, when len is even then len % 2
will return 0, and the code will jump to the else
. OP has it the other way around and it is correct. Better yet have len % 2 === 0
so that it is easier to understand at a glance, and not have to think about len % 2
returning 1 or 0 which then evaluates to true or false, etc..
For reference, here's the median()
function of lodash.math:
math.median = function(arr) {
arr = arr.slice(0); // create copy
var middle = (arr.length + 1) / 2,
sorted = math.sort(arr);
return (sorted.length % 2) ? sorted[middle - 1] : (sorted[middle - 1.5] + sorted[middle - 0.5]) / 2;
};
nice !
Similar to lodash.math
but without the custom sort method and a little more readable (in my opinion):
function median(numbers) {
const middle = (numbers.length + 1) / 2;
const sorted = [...numbers].sort((a, b) => a - b); // avoid mutating when sorting
const isEven = sorted.length % 2 === 0;
return isEven ? (sorted[middle - 1.5] + sorted[middle - 0.5]) / 2 : sorted[middle - 1];
}
EDIT: fixed as per @henrikra's comment.
@sqren, Thanks, worked like a charm.
@sqren Otherwise you answer was good but the number sorting didn't work
This version will work!
function median(numbers: number[]) {
const middle = (numbers.length + 1) / 2;
const sorted = [...numbers].sort((a, b) => a - b); // you have to add sorting function for numbers
const isEven = sorted.length % 2 === 0;
return isEven ? (sorted[middle - 1.5] + sorted[middle - 0.5]) / 2 : sorted[middle - 1];
}
@danielbayerlein sort
will mutate the array. I used the spread operator to instead return a new shallow copy. Before the spread operator concat
and slice
did the trick:
arr.concat().sort()
# or
arr.slice(0).sort()
Some more discussion on this topic https://stackoverflow.com/questions/9592740/how-can-you-sort-an-array-without-mutating-the-original-array
@sqren Thank you for the explanation.
@danielbayerlein You are welcome.
Btw. Noticed your project https://github.com/danielbayerlein/git-pick. I've create a tool called backport
: https://github.com/sqren/backport
Looks like we are doing something similar :D
I would also check the length first. if it is zero return. no need to proceed further.
This did not work for me. First, the code should account for zero-based index of the array, second, float value should not be passed as an array index (gecko js engine won't take it). Here is the correct code: