Sorting an array in javascript - javascript

var num_list = [1, 2, 3, 4];
function num_order(a, b) {return b-a; }
num_list.sort(num_order);
I've been through blogs and i have searched on this topic but to no avail. All describe this function to be sorting in descending order but none specify how does that happen..
For example, what values are stored in the parameters a and b and how are these values assigned.. Finally what results the function passes and how does sort method do to those values.. Any help would be appreciated..
Let me specify that there is a post similar to this but the answer in that post is not clear.. One of the user has provided a link here which makes it much clearer

The parameter you pass to the sort method is the comparison function. It will define the order the elements are sorted.
To see what values are being passed to the parameters a and b. Try this in your console:
var num_list = [1, 2, 3, 4];
num_list.sort(function(a, b) {
debugger;
return b-a;
})
MDN has good documentation on the compare function: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Have a look at any sort algorithm : at some point they need to compare two elements of the array.
The sort function of most Browsers is heapsort or quicksort, but i will take bubble sort as an example of a sort algorithm :
n = array size
repeat
swapped = false
for i= 0 to n-2
if array [i] > array [i+1] then
swap ( array [i] , array [i+1] )
swapped = true
end for
n = n -1
until swapped = false
we can easily rewrite the comparison to use a comparison function :
n = array size
repeat
swapped = false
for i= 0 to n-2
a = array [i]
b = array [i+1]
if compareFunction(a,b) > 0 then
swap ( array [i] , array [i+1] )
swapped = true
end for
n = n -1
until swapped = false
with :
compareFunction (a,b)
return a-b
So the comparison function is just a function that returns an integer that reflect the items order.
If two elements are the same => the function returns 0, first is bigger => returns >0, second is bigger => returns <0.
Using a comparison function allows to sort any kind of array (i.e. an array containing any kind of item), while still using the very same sort algorithm as the one used for integer sort.

Related

Javascript sort method for arrays [duplicate]

How does the following code sort this array to be in numerical order?
var array=[25, 8, 7, 41]
array.sort(function(a,b){
return a - b
})
I know that if the result of the computation is...
Less than 0: "a" is sorted to be a lower index than "b".
Zero: "a" and "b" are considered equal, and no sorting is performed.
Greater than 0: "b" is sorted to be a lower index than "a".
Is the array sort callback function called many times during the course of the sort?
If so, I'd like to know which two numbers are passed into the function each time. I assumed it first took "25"(a) and "8"(b), followed by "7"(a) and "41"(b), so:
25(a) - 8(b) = 17 (greater than zero, so sort "b" to be a lower index than "a"): 8, 25
7(a) - 41(b) = -34 (less than zero, so sort "a" to be a lower index than "b": 7, 41
How are the two sets of numbers then sorted in relation to one another?
Please help a struggling newbie!
Is the array sort callback function called many times during the course of the sort?
Yes
If so, I'd like to know which two numbers are passed into the function each time
You could find out your self with:
array.sort((a,b) => {
console.log(`comparing ${a},${b}`);
return a > b ? 1
: a === b ? 0
: -1;
});
EDIT
This is the output I've got:
25,8
25,7
8,7
25,41
The JavaScript interpreter has some kind of sort algorithm implementation built into it. It calls the comparison function some number of times during the sorting operation. The number of times the comparison function gets called depends on the particular algorithm, the data to be sorted, and the order it is in prior to the sort.
Some sort algorithms perform poorly on already-sorted lists because it causes them to make far more comparisons than in the typical case. Others cope well with pre-sorted lists, but have other cases where they can be "tricked" into performing poorly.
There are many sorting algorithms in common use because no single algorithm is perfect for all purposes. The two most often used for generic sorting are Quicksort and merge sort. Quicksort is often the faster of the two, but merge sort has some nice properties that can make it a better overall choice. Merge sort is stable, while Quicksort is not. Both algorithms are parallelizable, but the way merge sort works makes a parallel implementation more efficient, all else being equal.
Your particular JavaScript interpreter may use one of those algorithms or something else entirely. The ECMAScript standard does not specify which algorithm a conforming implementation must use. It even explicitly disavows the need for stability.
Pairs of values are compared, one pair at a time. The pairs that are compared are an implementation detail--don't assume they will be the same on every browser. The callback can be anything (so you can sort strings or Roman numerals or anything else where you can come up with a function that returns 1,0,-1).
One thing to keep in mind with JavaScript's sort is that it is not guaranteed to be stable.
Deeply Knowledge
If the result is negative a is sorted before b.
If the result is positive b is sorted before a.
If the result is 0 no changes are done with the sort order of the two values.
NOTE:
This code is the view inside of the sort method step by step.
OUTPUT:
let arr = [90, 1, 20, 14, 3, 55];
var sortRes = [];
var copy = arr.slice(); //create duplicate array
var inc = 0; //inc meant increment
copy.sort((a, b) => {
sortRes[inc] = [ a, b, a-b ];
inc += 1;
return a - b;
});
var p = 0;
for (var i = 0; i < inc; i++) {
copy = arr.slice();
copy.sort((a, b) => {
p += 1;
if (p <= i ) {
return a - b;
}
else{
return false;
}
});
p = 0;
console.log(copy +' \t a: '+ sortRes[i][0] +' \tb: '+ sortRes[i][1] +'\tTotal: '+ sortRes[i][2]);
}
To help clarify the behavior of Array#sort and its comparator, consider this naive insertion sort taught in beginning programming courses:
const sort = arr => {
for (let i = 1; i < arr.length; i++) {
for (let j = i; j && arr[j-1] > arr[j]; j--) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]];
}
}
};
const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array);
console.log("" + array);
Ignoring the choice of insertion sort as the algorithm, focus on the hardcoded comparator: arr[j-1] > arr[j]. This has two problems relevant to the discussion:
The > operator is invoked on pairs of array elements but many things you might want to sort such as objects don't respond to > in a reasonable way (the same would be true if we used -).
Even if you are working with numbers, oftentimes you want some other arrangement than the ascending sort that's been baked-in here.
We can fix these problems by adding a comparefn argument which you're familiar with:
const sort = (arr, comparefn) => {
for (let i = 1; i < arr.length; i++) {
for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]];
}
}
};
const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array, (a, b) => a - b);
console.log("" + array);
sort(array, (a, b) => b - a);
console.log("" + array);
const objArray = [{id: "c"}, {id: "a"}, {id: "d"}, {id: "b"}];
sort(objArray, (a, b) => a.id.localeCompare(b.id));
console.log(JSON.stringify(objArray, null, 2));
Now the naive sort routine is generalized. You can see exactly when this callback is invoked, answering your first set of concerns:
Is the array sort callback function called many times during the course of the sort? If so, I'd like to know which two numbers are passed into the function each time
Running the code below shows that, yes, the function is called many times and you can use console.log to see which numbers were passed in:
const sort = (arr, comparefn) => {
for (let i = 1; i < arr.length; i++) {
for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]];
}
}
};
console.log("on our version:");
const array = [3, 0, 4, 5];
sort(array, (a, b) => console.log(a, b) || (a - b));
console.log("" + array);
console.log("on the builtin:");
console.log("" +
[3, 0, 4, 5].sort((a, b) => console.log(a, b) || (a - b))
);
You ask:
How are the two sets of numbers then sorted in relation to one another?
To be precise with terminology, a and b aren't sets of numbers--they're objects in the array (in your example, they're numbers).
The truth is, it doesn't matter how they're sorted because it's implementation-dependent. Had I used a different sort algorithm than insertion sort, the comparator would probably be invoked on different pairs of numbers, but at the end of the sort call, the invariant that matters to the JS programmer is that the result array is sorted according to the comparator, assuming the comparator returns values that adhere to the contract you stated (< 0 when a < b, 0 when a === b and > 0 when a > b).
In the same sense that I have the freedom to change my sort's implementation as long as I don't breach my specification, implementations of ECMAScript are free to choose the sort implementation within the confines of the language specification, so Array#sort will likely produce different comparator calls on different engines. One would not write code where the logic relies on some particular sequence of comparisons (nor should the comparator produce side effects in the first place).
For example, the V8 engine (at the time of writing) invokes Timsort when the array is larger than some precomputed number of elements and uses a binary insertion sort for small array chunks. However, it used to use quicksort which is unstable and would likely give a different sequence of arguments and calls to the comparator.
Since different sort implementations use the return value of the comparator function differently, this can lead to surprising behavior when the comparator doesn't adhere to the contract. See this thread for an example.
Is the array sort callback function called many times during the course of the sort?
Yes, that's exactly it. The callback is used to compare pairs of elements in the array as necessary to determine what order they should be in. That implementation of the comparison function is not atypical when dealing with a numeric sort. Details in the spec or on some other more readable sites.
Is the array sort callback function called many times during the course of the sort?
Since this is a comparison sort, given N items, the callback function should be invoked on average (N * Lg N) times for a fast sort like Quicksort. If the algorithm used is something like Bubble Sort, then the callback function will be invoked on average (N * N) times.
The minimum number of invocations for a comparison sort is (N-1) and that is only to detect an already sorted list (i.e. early out in Bubble Sort if no swaps occur).
Is the array sort callback function called many times during the course of the sort?
Yes
If so, I'd like to know which two numbers are passed into the function each time.
a: The first element for comparison.
b: The second element for comparison.
In the following example, a will be "2" and b will be "3" in the first iteration
How are the two sets of numbers then sorted in relation to one another?
Elements are sorted according to the return value of the compare function.
greater than 0: sort a after b
less than 0: sort a before b
equal to 0: keep original order of a and b
Here is an example
var arr = [3, 2, 1, 5, 4, 6, 7, 9, 8, 10];
console.log(arr.sort((a, b) => {
console.log(a - b, a, b);
//b-a if sorting in decending order
return a - b;
}));

JavaScript minus zero changes results

I was playing around with Arrays in jsfiddle and noticed that when I do this:
let a = [0,1,2,3,4];
for (let i in a) {
console.log(a.indexOf(i));
}
it logs:
But when I do:
let a = [0,1,2,3,4];
for (let i in a) {
console.log(a.indexOf(i - 0));
}
it logs:
Minus zero changed the result!
At first I thought it was a jsfiddle problem, but then I tried it with my code editor the same thing happened. Why does this happen can someone explain to me?
When you do:
for (let i in a) ...
This will iterate over indexes of the array as strings. It treats the array like an object and iterates over the indexes like keys. You will get strings: "0", "1"...
You can see that if you print the value and type:
let a = [0,1,2,3,4];
for (let i in a) {
console.log(i, typeof i)
}
Those strings are not in your array — your array has numbers — so findIndex() doesn't find them and gives you -1. However when you subtract 0, javascript converts to an integer for you and suddenly it finds them because the indexes match the values.
What you probably want is for...of to iterate over the values:
let a = [0,1,2,3,4];
for (let i of a) {
console.log("i:", i, typeof i)
console.log("index:", a.indexOf(i));
}
For... in loop is meant to use on objects. In your particular case, the i element in the loop is not a value from the array, but it's key.
Key is typeof string and since you don't have any strings in your array, you are getting -1 with every cycle.
Using - 0 evaluates the string into a number.
Note: It's an unique and "happy" situation, since your elements in the array are sorted, iterated integers starting at 0. That's why it seems to work properly.
Just a quick showcase:
const keys = Object.assign({}, [0, 1, 2, 3]);
console.log(keys);
console.log(typeof Object.keys(keys)[0]);
Snippet above represents how your array is interpreted by for... in loop. As you can easily see, every key from such object is a string.
Please have a look:
let a = [0,1,2,3,4];
for (let i in a) {
console.log(a.indexOf(a[i]));
}

sorting an array and comparing it to the original

I'm trying to fully understand functional programming when sorting an array why does the original array change to the sorted array also? I
want to check if the array is in ascending order.
let arr = [1,2,8,3,9,88,67];
let newArr = arr.sort((a,b) => a-b);
console.log(newArr);
console.log(arr);
I want to do something like....
if (arr === newArr) {
return true;
} else {
return false;
}
The original arr is also being sorted so it always is true,
some guidance would be great thanks.
That's just the way the sort operator was designed. It modifies the existing array, rather than creating a new array. If you want to create a new array, you can do it like this:
let arr = [1,2,8,3,9,88,67];
let newArr = arr.slice(); // creates a copy of the array
newArr.sort((a,b) => a-b);
console.log(newArr);
console.log(arr);
Javascript array is an object, When 2 variables reference the same object, changing one would change the value of the other
let obj1 = {x:1, y:2};
let obj2 = obj1;
obj2.x = 3;
console.log(obj1);
You can sort it using
let arr = [1,2,8,3,9,88,67];
let newArr = arr.slice().sort((a,b) => a-b);
console.log(newArr);
console.log(arr);
By assigning an object, you take the reference of the object, that means ecery change affects the same object.
For arrays, you could take "a shallow copy" of it with Array#slice and then map the check of the items.
var array = [1, 2, 8, 3, 9, 88, 67],
sorted = array.slice().sort((a, b) => a - b),
position = array.map((a, i) => a === sorted[i]);
console.log(sorted);
console.log(position);
.as-console-wrapper { max-height: 100% !important; top: 0; }
According to your question,
you just need to identify
If the array is in ascending order
To do that just apply some simple logic and we do not want to compare it with a sorted array.
This is the speediest method since it will break the loop on wrong condition.
let arr = [1,2,8,3,9,88,67];
let is_sorted = true;
for(let i=0, length = arr.length; i < length - 1; i++){
if(Number(arr[i]) > Number(arr[i+1])){
is_sorted = false;
break;
}
}
console.log("Is Sorted: ", is_sorted);
This is an XY problem – ie, you want "... to check if the array is in ascending order" and you tried sorting (via Array.prototype.sort) and then you're checking the result using binary operator ==. The result isn't what you expect, so you ask about the sort instead of keeping the focus on your actual goal: checking for ascending order
A case for why you shouldn't sort
Imagine a large array of hundreds or thousands or items. Is the following array in ascending order?
isAscending ([ 5, 1, ... thousands more items ... ])
// => true or false ?
Of course it's false because 1 is less than 5; the array is not in ascending order. Did we have to sort the entire array to arrive at that answer? No, we only needed to look at the first 2 items (in this case) to know the answer is false
Other answers show using .slice to copy the input array – this is silly tho, because of course we don't have to copy an array to determine if it is in ascending order – it's a waste of time/space.
A case for why you shouldn't use ==
Well first, you can't, because of the following (in JavaScript)
[ 1, 2, 3 ] == [ 1, 2, 3 ]
// => false
So how would use == if you could? Maybe an approach would be to check if each element in the first array is equal to each array element in the second array. Ok, now imagine two large arrays of hundreds of thousands of items. Are the following two arrays equal?
[ 1, ... thousands of items ... ] == [ 2, ... thousands of items ... ]
// => true or false ?
We know they're not equal because 1 != 2 – it's the same case as the sorting; there's no sense in comparing the rest of the items because we already know the arrays are not equal
There's other ways to compare Arrays (and objects) in JavaScript, but we only have one input array, so there's no array to compare it to – this is another dead-end to the approach
Check if an array is in ascending order
Ok, so now that were done talking about array sorting and array equality in JavaScript, we can actually write a function that does what you intend it to do
const isAscending = ([x,y,...rest]) =>
x === undefined // array is empty
? true
: y === undefined // single element array
? true
: x > y // first element is larger than second
? false
: isAscending ([y,...rest]) // check remaining elements
console.log (isAscending ([])) // true
console.log (isAscending ([1])) // true
console.log (isAscending ([1,3,5,7])) // true
console.log (isAscending ([5,1,3,7])) // false
Stack-safe recursion
But you have to be careful using recursion in JavaScript – input arrays of just a couple thousand elements could cause a stack overflow in the program above
Using a constant-space looping mechanism, we can rewrite isAscending in a way that works on arrays of any input size. The loop/recur interface gives us an opportunity to track any number/type of state variables, so this implementation also avoids the costly creation of many array slices (in rest parameter and spread argument syntax above)
const recur = (...args) =>
({ type: recur, args })
const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}
const isAscending = xs =>
xs.length < 2
? true
: loop ((a = 0, b = 1) =>
a === xs.length
? true
: xs [a] > xs [b]
? false
: recur (a + 1, b + 1)) // *
console.log (isAscending ([])) // true
console.log (isAscending ([1])) // true
console.log (isAscending ([1,3,5,7])) // true
console.log (isAscending ([5,1,3,7])) // false
* or recur (b, b + 1), which saves on addition operation per array element
The sort method applied to an array will modify the array itself. So it's logic to have the first array sorted and both arr and newArr will be equal.
To test if the array is in ascending order you may loop through it and check if there is and index i where arr[i+1] < arr[i]
let arr = [1,2,8,3,9,88,67];
let test=true;
for (var i = 1; i < arr.length; i++) {
if(arr[i]<arr[i-1]) {
test=false;
break;
}
}
console.log(test);

Flattening Array In JavaScript- Explanation needed

I'm reading a book called Eloquent JavaScript. There's an exercise in it that requires one to flatten a heterogeneous array & after trying so long and failing to get the answer, I looked up the solution online & couldn't understand the code. I'm hoping someone will be kind enough to explain, especially for argument "flat" and how it's supposed to work. The code is below
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
return flat.concat(current);
}, []));
The reduce function defined in the book is:
function reduce(array, combine, start) {
var current = start;
for (var i = 0; i < array.length; i++)
current = combine(current, array[i]);
return current;
}
and as a method of an array,
arr.reduce(combine, start);
Let's look at each part of the reduce method. The book describes it as "folding up the array, one element at a time." The first argument for reduce is the "combiner function", that accepts two arguments, the "current" value and the "next" item in the array.
Now, the initial "current" value is given as the second argument of the reduce function, and in the solution of flattening arrays, it is the empty array, []. Note that in the beginning, the "next" item in the array is the 0th item.
Quoting the book to observe: "If your array contains at least one element, you are allowed to leave off the start argument."
It may also be confusing that in the flattening solution, current is placed as the second argument to reduce, whereas in the reduce definition above, current is used to assign the cumulative, folded value. In the flattening solution, current refers to the "next" arrays item (the individual array of integers)
Now, at each step of the reduction, the "current" value plus the next array item is fed to the (anonymous) combiner, and the return value becomes the updated "current" value. That is, we consumed an element of the array and continue with the next item.
flat is merely the name given to the accumulated result. Because we wish to return a flat array, it is an appropriate name. Because an array has the concat function, the first step of the reduce function is, (pretending that I can assign the internal variables)
flat = []; // (assignment by being the second argument to reduce)
Now, walk through the reduction as iterating over arrays, by going through the steps shown above in reduce's definition
for (var i = 0; i < arrays.length; i++)
flat = combine(flat, arrays[i]);
Calling combine gives [].concat([1, 2, 3]) // => [1, 2, 3]
Then,
flat = [1, 2, 3].concat([4, 5]) // => [1, 2, 3, 4, 5]
and again for the next iteration of the reduction. The final return value of the reduce function is then the final value of flat.
This would be the solution I came with with ES6 format:
const reduced = arrays.reduce((result,array) => result.concat(array),[]);
console.log(reduced);
I have implemented this solution and this seems to work for nested arrays as well.
function flattenArray(arr){
for(var i=0;i<arr.length;i++){
if(arr[i] instanceof Array){
Array.prototype.splice.apply(arr,[i,1].concat(arr[i]))
}
}
return arr;
}
There is an easy way to do these exercises. those functions are already built inside the javascript so you can use them easily.
But the whole joy of this exercise is to create those functions:
Create reduce function. Reduce function should add all array elements. you can use a higher-order function or just a normal one. here is an example for higher-order:
function reduce(array, calculate){
let sumOfElements = 0;
for(let element of array){
sumOfElements = calculate(sumOfElements, element)
}
return sumOfElements
}
Next step is to create a concat function. since we need to return those reduced arrays in new array we will just return them. (Warning: you must use rest parameter)
function concat(...arr){
return arr
}
And for last. you will just display it (You can use any example)
console.log(concat(reduce([1, 2, 3, 4], (a, b) => a + b), reduce([5, 6], (a, b) => a + b)))
The reduce method acts as a for loop iterating over each element in an array. The solution takes each array element and concatenates it to the next one. That should flatten the array.
var arr =[[1,2],[3,4],[5,6]]
function flatten(arr){
const flat= arr.reduce((accumulator,currentValue)=>{
return accumulator.concat(currentValue)
})
return flat
}
console.log(flatten(arr))
//Output 1,2,3,4,5,6

Javascript reduce function/ternary operator

function largestInEach(arr) {
var resultArray = [],
highestValue = 0;
for (var i = 0; i < arr.length; i++) {
highestValue = arr[i].reduce(function(a, b){
return a >= b ? a : b;
});
resultArray.push(highestValue);
}
return resultArray;
}
Can someone please explain this code.
The rest of the code is very clear to me, but I have difficulty understanding the reduce function and its application.
I agree with most of the other comments that you should search more and do self learning.
However, I know it is sometimes hard to find the exact info on how things work. So ill explain this to you.
Now coming to your code.
https://jsfiddle.net/Peripona/ppd4L9Lz/
It contains an array of arrays where you at the end create a resulted array with highest or lowest value elements from all the sub arrays.
like you got
var arry = [[1,2,3],[2,3,4],[20,-2,3]]
talking in layman terms...
you got one array if you sort or reduce an array of integers, it might not always generate what you
say for example if you got this data
var ar = [1,3,34,11,0,13,7,17,-2,20,-21]
and if you do normal ar.sort() to get the sorted values
you would expect something like this... as output
" [-21, -2, 0, 1, 3, 7, 11, 13, 17, 20, 34] "
but on the contrary you would get the output like this..
" [-2, -21, 0, 1, 11, 13, 17, 20, 3, 34, 7] "
Now you wonder... why this strange behavior and how does it matter anyhow to my Question..
It Does matter..
Cuz this is what you need to do to get the right output..
The way sort function is written has to work for for String and other types as well. so they convert data into other formats when doing comparison on sort.
So all in all if you pass a function inside and specify that you need the in ascending order that is a-b..
Descending order that is b-a..
ar.sort(function(a,b){return a-b;})
Now coming to another part that is Reduce this function takes a function argument and get you the highest or the lowest value from the array.
therefore if you do..
ar.reduce(function(a,b){return a>=b ? b : a})
will give you -21 as the output..
and if you do
ar.reduce(function(a,b){return a>=b ? a : b})
It will give you : 34
So this function will take multidimensional arrays where each array contains some digits and this function will get you the highest from all those arrays..
I hope this Explains everything.
Reduce function allows you to go through each item in an array, where you will be able to see previous array value, and current array value, in your case:
a = previous value,
b = current value,
-(not in there)-
i = index,
currArray = the array you are working with.
and in your code you are comparing and returning if your previous value is greater than or equal to current value.
a >= b ? a : b;
Conditional (ternary) Operator which is (condition ? do this : or this ) -> Think of it like a if statement
If(a >= b){
return a
}else{
return b
}
see Conditional (ternary) Operator
Also your 'arr' could be multi dimensional array. forexample Trying the following code on http://plnkr.co/edit/?p=preview
hit f12 for developer tools and look at console for results.
var arr = [[1,2],[4,3],[5,23,52]];
var resultArray = [],
var highestValue;
for (var i = 0; i < arr.length; i++) {
highestValue = arr[i].reduce(function(a, b){
return a >= b ? a : b;
});
resultArray.push(highestValue);
}
console.log(resultArray);
You result array contains [2, 4, 52].
I hope this helps.
JS reduce method is applied against two values of array and reduce these two values of array ( a and b) into one (c) based on defined condition (return c = a+b ).
Here in your case the condition was which among two is greater (a>b?a:b).

Categories