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;
}));
Related
I am writing a function that takes an array and an integer number and returns an array of subarrays. The number of subarrays is exact the integer number passed to the function. And the subarrays have to be continuous, meaning the original order of items in the array has to be preserved. Also no subarray can be empty. They have to have at least one item in it. For example:
const array = [2,3,5,4]
const numOfSubarray = 3
const subarrays = getSubarrays(arraym numOfSubarray)
In this case subarrays is this:
[
[[2, 3], [5], [4]],
[[2], [3, 5], [4]],
[[2], [3], [5, 4]],
]
Here is my attempt:
function getSubarrays(array, numOfSubarray) {
const results = []
const recurse = (index, subArrays) => {
if (index === array.length && subArrays.length === numOfSubarray) {
results.push([...subArrays])
return
}
if (index === array.length) return
// 1. push current item to the current subarray
// when the remaining items are more than the remaining sub arrays needed
if (array.length - index - 1 >= numOfSubarray - subArrays.length) {
recurse(
index + 1,
subArrays.slice(0, -1).concat([subArrays.at(-1).concat(array[index])])
)
}
// 2. start a new subarray when the current subarray is not empty
if (subArrays.at(-1).length !== 0)
recurse(index + 1, subArrays.concat([[array[index]]]))
}
recurse(0, [[]], 0)
return results
}
Right now it seems to be working. But I wanted to know what is the time/space complexity of this algorithm. I think it is definitely slower than O(2^n). Is there any way to improve it? Or any other solutions we can use to improve the algorithm here?
You can't get an answer down to anything like 2n, I'm afraid. This grows much faster than that, because the answer has to do with the binomial coefficients, whose definitions have fundamental factorial parts, and whose approximations involve terms like nn.
Your solution seems likely to be worse than necessary, noted because of the exponential number of calls required to solve the simplest case, when numOfSubarrays is 1, and you should just be able to return [array]. But as to full analysis, I'm not certain.
As the first comment shows, the above analysis is dead wrong.
However, if your're interested in another approach, here's how I might do it, based on the same insight others have mentioned, that the way to do this is to find all sets of numOfSubarrays indices of the positions between your values, and then convert them to your final format:
const choose = (n, k) =>
k == 0
? [[]]
: n == 0
? []
: [... choose (n - 1, k), ... choose (n - 1, k - 1). map (xs => [...xs, n])]
const breakAt = (xs) => (ns) =>
[...ns, xs .length] .map ((n, i) => xs .slice (i == 0 ? 0 : ns [i - 1], n))
const subarrays = (xs, n) =>
choose (xs .length - 1, n - 1) .map (breakAt (xs))
console .log (subarrays ([2, 3, 5, 4], 3) // combine for easier demo
.map (xs => xs .map (ys => ys .join ('')) .join('-')) //=> ["23-5-4", "2-35-4", "2-3-54"]
)
console .log (subarrays ([2, 3, 5, 4], 3)) // pure result
.as-console-wrapper {max-height: 100% !important; top: 0}
Here, choose (n, k) finds all the possible ways to choose k elements from the numbers 1, 2, 3, ..., n. So, for instance, choose (4, 2) would yield [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]].
breakAt breaks an array into sub-arrays at a set of indices. So
breakAt ([8, 6, 7, 5, 3, 0, 9]) ([3, 5])
// 3 5 ///=> [[8, 6, 7], [5, 3], [0, 9]]
And subarrays simply combines these, subtracting one from the array length, subtracting one from numOfSubarrays, calling choose with those two values, and then for each result, calling breakAt with the original array and this set of indices.
Even here I haven't tried to analyze the complexity, but since the output is factorial, the process will take a factorial amount of time.
If you want to completely split a list of n elements into k disjunct, continuous sub-lists this is like placing k-1 split points into the n-1 gaps between the elements:
2 | 3 | 5 4
2 | 3 5 | 4
2 3 | 5 | 4
In combinatorics this is taking k-1 from n-1. So I think the result size of the ouput will be n-1 take k-1 = (n-1)! / ((k-1)! * (n-k)!). Thus the complexity is something polynomial like O(n^(k-1)) for constant k. If you don't fix k but raise it with n like k = n/2 the complexity will get exponential.
I don't think that you can improve this, because the output's size is increasing by this complexity.
tl;dr
The number of solutions is bound to (as #gimix mentioned) binomial coefficient, so if I understand correctly it's pessimistically exponential
https://en.wikipedia.org/wiki/Binomial_coefficient#Bounds_and_asymptotic_formulas.
If I'm not mistaken that makes your algorithm this exponential * n (for each element of each solution) * n (because on nearly every step you copy array which length might be dependent on n).
fix second if - only call recurse if subArrays.length < numOfSubarrays
you are copying arrays a lot - slice, concat, spread operator - all of those create new arrays. If for every solution (which length might be depending on n) on every step you copy this solution (which I think is happening here) you multiply the complexity by n.
the space complexity is also exponential * n - you store the exponential number of solutions, possibly of length dependent on n. Using a generator and returning one solution at the time could greatly improve that. As #gimix mentioned the combinations might be the simplest way to do it. Combinations generator in python: https://docs.python.org/3/library/itertools.html#itertools.combinations
Dwelling on complexity:
I think you are right about the slower than exponential complexity, but - bare with me - how much do you know about Fibonacci's sequence? ;)
Let's consider input:
array = [1, 2, ..., n]
numOfSubarrays = 1
We can consider the recursive calls a binary tree with if 1. guarding the left child (first recurse call) and if 2. guarding the right child (second recurse call).
For each recurse call if 1. will be fulfilled - there are more items than sub arrays needed.
Second if will be true only if current sub array has some elements. It's a tricky condition - it fails if, and only if, it succeeded one frame higher - an empty array has been added at the very beginning (except for the root call - it has no parent). Speaking in terms of a tree, it means we are in the right child - the parent must have just added an empty sub array as a current. On the other hand, for the left child parent has just pushed (yet another?) element to the current sub array and we are sure the if 2. will succeed.
Okay, but what does it say about the complexity?
Well, we can count the number of nodes in the tree, multiply by the number of operations they perform - most of them a constant number - and we get the complexity. So how many are there?
I'll keep track of left and right nodes separately on every level. It's gonna be useful soon. For convenience I'll ignore root call (I could treat it as a right node - it has empty sub array - but it messes up the final effect) and start from level 1 - the left child of the root call.
r1 = 0
l1 = 1
As a left node (sub array isn't empty) it has two children:
r2 = 1
l2 = 1
Now, the left node always has two children (1. is always fulfilled; 2. is true because parent pushed element to current sub array) and the right node has only the left child:
r3 = r2 + l2 = 1 + 1 = 2
l3 = r2 = 1
we could continue. The results are:
l
r
1
0
1
1
2
1
3
2
5
3
well... it's oddly familiar, isn't it?
Okay, so apparently, the complexity is O(Σ(Fi + Fi-1) where 1 <= i <= n).
Alright, but what does it really mean?
There is a very cool prove that S(n) - sum of the Fibonacci numbers from 0 to n is equal F(n+2) - 1. It simplifies the complexity to:
O(S(n) + S(n-1)) = O(F(n+2) - 1 + F(n+1) - 1) = O(F(n+3) - 2) = O(F(n+3))
We can forget about the +3 since F(n+3) < 2 * F(n+2) < 4 * F(n+1) < 8 * F(n).
The final question, is Fibonacci sequence exponential? Yes and no apparently.
The is no number that would fulfil the xn = F(n) - the value oscillates between 2 and √2, because for F(n+1) < 2 * F(n) < F(n+2).
It's proven though, that lim(n->∞) F(n+1) / F(n) = φ - the golden ratio. It means the O(F(n)) = O(φn). (Actually, you copy arrays a lot, so it's more like O(φn*n))
How to fix it? You could check if there isn't too many arrays before recursing in if 2.
Other than that, just as #Markus mentioned, depending on the input, the number of solutions might be exponential, so the algorithm to get them also has to be exponential. But that's not true for every input, so let's keep those cases to minimum :D
The problem can be solved by a different approach:
compute all the combinations of numOfSubarray numbers ranging from 1 to the length of array
each combination is a valid slicing of array. For instance, you want to slice the array in your example at positions (1, 2), (1, 3), (2, 3), yielding subarrays [[2],[3],[5,4]], [[2],[3,5],[4]], and [[2,3],[5],[4]]
Time complexity is, I believe, O(r(nCr)) where n is (length of array - 1) and r is (number of subarrays - 1).
To visualize how and why it works have a look at the stars and bars technique
I have an array which looks like this
arr = ["#4abc", "#2xyz", "#3pol", "#33pe", "#1bfj", "#11lo", "#2mao"]
Each element in the array is associated with a #n. Now I want to sort them in ascending order.
So this is what I do
arr.sort( (a,b) => a.match(/#(\d)/)[1] - b.match(/#(\d)/)[1])
Gives me the expected output
arr ['#1bfj', '#11lo', '#2xyz', '#2mao', '#3pol', '#33pe', '#4abc']
But I also want to keep the highest in the beginning such that it looks like this
arr ['#4abc', '#1bfj', '#11lo', '#2xyz', '#2mao', '#3pol', '#33pe']
Now I know #4 is the highest that can occur in an array. So this is what I tried
arr.sort((a, b) => (a.match(/#(\d)/)[1] === "4") - (b.match(/#(\d)/)[1] === "4") || a.match(/#(\d)/)[1] - b.match(/#(\d)/)[1]);
But I don't get the expected output. How can I do this?
Pop the highest element. Sort the rest. And do array.unshift(highest) which places string to the beginning
If your array is very long, then it becomes important to get a good time complexity. You can use buckets. In your case there are only four buckets: 4, 1, 2, 3. In that order.
So:
let arr = ["#4abc", "#2xyz", "#3pol", "#33pe", "#1bfj", "#11lo", "#2mao"];
let buckets = [[], [], [], []];
for (let val of arr) buckets[val[1] % 4].push(val);
let sorted = buckets.flat();
console.log(sorted);
Note how the % operator translates 4 to 0, while leaving 1, 2 and 3 unchanged, which is exactly what you need here.
When there are no "#4" in the data... Or higher digits occur
If you would have no "#4" in the data, and you would then want to have the "#3" come first, ...etc, or when there can be higher digits (in the range 0 through 9), then you would need a more dynamic bucket-solution:
let arr = ["#6abc", "#2xyz", "#3pol", "#9pe", "#0bfj", "#11lo", "#2mao"];
let buckets = [[], [], [], [], [], [], [], [], [], []]; // 10 buckets
for (let val of arr) buckets[val[1]].push(val);
buckets = buckets.filter(bucket => bucket.length); // remove empty buckets
let sorted = buckets.pop().concat(...buckets); // last comes first
console.log(sorted);
All these operations have a O(n) time complexity, compared to the O(nlogn) time complexity that you get from a call to sort. Now, if your data is not extreme in volume, a solution using sort will still run faster.
Given you want the one beginning with 4 to be the lowest ranked (first), you would need to use !== instead of ===:
arr.sort((a, b) => (a.match(/#(\d)/)[1] !== "4") - (b.match(/#(\d)/)[1] !== "4") || a.match(/#(\d)/)[1] - b.match(/#(\d)/)[1]);
// simplified:
arr.sort((a, b) => (!a.startsWith("#4") - (!b.startsWith("#4")) || a.match(/^#(\d)/)[1] - b.match(/^#(\d)/)[1]);
However, as suggested in the other answer, it's probably much easier to just sort all items normally, then move the last to front.
your solution works !
simply put your args in a different order (aka backward)
arr.sort( (a,b) => b.match(/#(\d)/)[1] - a.match(/#(\d)/)[1])
I've a multidimensional array
arr = [[[1,1],[2,1],[3,1],[4,2],[5,2],[6,2],[7,3],[8,4]],
[[1,1],[2,1],[3,1],[4,3],[5,3],[6,4],[7,4],[8,5],[9,5],[10,5]]
];
and so on ... but the dimension of the arr is not fixed, is variable.
I've a variable that tell me where to point my attention
var number = 2;
So my goal is the look in any arr[i] and find the max 1st argument based on the 2nd argument, I try to explain better my self, in this particular case if number is 2 my expectation is to have from arr:
for the 1st array in arr -> 6 (because the second argument is 1,1,1,2,2,2,3 so I've to point at the last 2 and return the 1st argument)
for the 2nd array in arr -> 3 (because 2 is missing and the 1 is the last second argument)
I know is a little tricky
My first idea was to make a for loops where I delete all value over my number, then I can take the very last one, but I think I'm over-complicating all.
There is a better and fast way to achieve the same result?
J
You present lists (arrays) of pairs of numbers, where the pairs are sorted in ascending order, first by the second number, then by the first.
What you seem to ask for is: Given a number to search for among the second numbers, e.g. number = 2, find the last pair where the second number is less than or equal to this number, and return the corresponding first number in this pair.
You state that you could use for loops to solve the problem. A straightforward approach could be like the following snippet:
var arr = [[[1,1],[2,1],[3,1],[4,2],[5,2],[6,2],[7,3],[8,4]],
[[1,1],[2,1],[3,1],[4,3],[5,3],[6,4],[7,4],[8,5],[9,5],[10,5]]
];
var findNumber = 2;
var result = [];
for(var i = 0; i < arr.length; i++){
var maxIndex = -1;
for(var j = 0;
j < arr[i].length && arr[i][j][1] <= findNumber;
j++){
maxIndex = j;
}
result.push(arr[i][maxIndex][0]);
}
//gives the expected answers 6 and 3
console.log(result);
Then you ask:
There is a better and fast way to achieve the same result?
A solution involving .map and .reduce could be considered more elegant, like the following:
var arr = [[[1,1],[2,1],[3,1],[4,2],[5,2],[6,2],[7,3],[8,4]],
[[1,1],[2,1],[3,1],[4,3],[5,3],[6,4],[7,4],[8,5],[9,5],[10,5]]
];
var findNumber = 2;
var result = arr.map(function(val){
return val[val.reduce(function(acc, curr, index){
return curr[1] <= findNumber? index : acc;
}, -1)][0];
});
//gives the expected answers 6 and 3
console.log(result);
However, in terms of performance, for loops are likely to perform better (run faster) and are easy to comprehend.
In addition, you mention that
the dimension of the arr is not fixed
You would need to post some code examples on how the dimensionality of your data may vary before it would be possible to provide any answer that handles this aspect.
Update
To handle a single array of pairs, you do not need the outer loop or .map(). Putting the solution above into a reusable function:
function lookupFirstNumberFromSecond(secondNumberToFind, arr){
var j = 0, maxIndex = -1;
while(j < arr.length && arr[j][1] <= secondNumberToFind){
maxIndex = j++;
}
return arr[maxIndex][0];
}
//gives the expected answer 6
console.log(lookupFirstNumberFromSecond(
2,
[[1,1],[2,1],[3,1],[4,2],[5,2],[6,2],[7,3],[8,4]]
));
//gives the expected answer 3
console.log(lookupFirstNumberFromSecond(
2,
[[1,1],[2,1],[3,1],[4,3],[5,3],[6,4],[7,4],[8,5],[9,5],[10,5]]
));
I'm not entirely sure about what you are trying to achieve but I guess Array.reduce is a pretty elegant solution to get a single value out of an array.
e.g.
var number = 2;
[[1,4],[2,1],[3,1],[4,2],[5,2],[6,2],[7,3],[8,4]]
.reduce(function (a, b) {
return ((!a || b[0] > a[0]) && b[1] === number) ? b : a;
});
Not entirely sure what you're trying to solve either, but if you're trying to get the max value in a n dimensional array, then the most straightforward method is to solve this standardly in a recursive manner
function recurseMax(arr) {
if (Number.isInteger(arr)) {
return arr;
}
else {
answer = 0;
for (let i = 0; i < arr.length; i++) {
answer = answer > recurseMax(arr[i]) ? answer : recurseMax(arr[i]);
}
return answer;
}
}
console.log(recurseMax([1,[3, 5], [5, 6, 7, 10], [2, [3, [500]]]])); //Outputs 500
For each element, either is a number or another possible multidimensional element, so we recursively find its max. This avoids potential overhead from a reduce operation (though I'm not experienced enough to speak with confidence whether or not it is completely faster, not really sure of the optimizations V8 can do on reduce or a plain old recursion loop). Either way, the solution is fairly straightforward.
I am answering the question based on the assumption that you mean that the array can have a max dimension of n.
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).
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.