Sort sting with numbers javascript - javascript

Hi can someone help me sort string with numbers in java script.
I want to sort these options using jquery according to their distance (9 km).
<select >
<option >Point a (9 km)</option>
<option >Point b (10 km)</option>
<option >Point c (2 km)</option>
</select>
Basically I just want to sort string that has some numbers in them.Another example.
input:
var arr = ['A (9 km)','B (10 km)','C (2 km)']
output:
C (2 km)
A (9 km)
B (10 km)
You got the point.
Thanks in advance,

Is this what you're after ?
const distances = ['A (9 km)','B (10 km)','C (2 km)'];
const sorted = distances
.map((x,i)=>[parseInt(x.replace(/[^\d]+/g,''),10),i])
.sort((a,b)=>a[0]-b[0])
.map(x=>distances[x[1]]);
console.log(sorted);

First we need to assign references to our DOM nodes. We'll call the parent selectContainer and its list of children to selectChildren.
Next we need a Regular Expression pattern to extract the value of interest that we're using to apply the sorting logic to. In this case we're extracting the digit number in the parenthesized km.
Finally, we cast the children elements to an array so it is iterable, allowing us to use the sort method.
Inside our sort method we are using our Regular Expression pattern to extrapolate the number value we care about.
const selectContainer = document.querySelector('select');
const selectChildren = selectContainer.querySelectorAll('option');
const pattern = /.*\((\d+)\skm\).*/;
const sorted = [...selectChildren].sort((a, b) => {
const _a = parseInt(a.innerText.match(pattern)[1]);
const _b = parseInt(b.innerText.match(pattern)[1]);
return _a < _b ? -1 : 1;
});
sorted.forEach(child => selectContainer.append(child));

Since you mentioned, multiple use cases, let's talk about the sorting parts. Once you have a collection of the strings, and the numbers, perhaps something in the form tuple, like so:
var pairs = [
[9, 'A (9 km)'],
[5, 'B (5 km)'],
[5, 'C (5 km)']
];
You can tackle the problem in any number of ways, let's start with a few simple
built in array functions, such as sort and map.
Sort, is an array function which takes a "comparator" function, which you design, to sort an array.
Here is a simple example of the "comparator" function, assuming you have a collection of tuples, as I outlined above:
function compareDistanceTuples(a, b) {
// is A less than B?
if (a[0] < b[0]) {
return -1;
}
// is A grater than B?
if (a[0] > b[0]) {
return 1;
}
// A must be equal to B
return 0;
}
Now you can use sort and map, along with your new "comparator" to generate your
sorted list, like so:
var sorted = pairs.sort(compareDistanceTuples).map(function(tuple){
return tuple[1];
});

You cant expect to write a generic function that will know how to compare any string based on any numbers inside of it. Trying to sort any kind of string this way is that it is very specific to your use case. Here, we have to believe that the number we care about will be inside of parenthesis and followed by km.
Sorting Requires providing a comparison function to your array.sort(). Given this specific use case, I used a regex to pull out the number from your string and then compare numbers.
//input:
var arr = ['A (9 km)', 'B (10 km)', 'C (2 km)'];
arr.sort((a, b) => {
const regEx = /\((\d+)\s{1}km\)/;
const aNumMatch = a.match(regEx);
const aNum = aNumMatch[1]
const bNumMatch = b.match(regEx);
const bNum = bNumMatch[1];
return (aNum - bNum);
});
for(let index = 0; index < arr.length; index++){
console.log(arr[index]);
}

You can sort any array in-place using Array.prototype.sort.
The meat of the operation is extracting the number from the string, which I assume you can do already based on another comment. Let's assume your function that can do this pointToNumber(pointStr)
For anyone else coming to this question later, Number.parseInt(str.replace(/[^\d]/g, ''), 10) would probably accomplish it if you know that your string will only have a single number in it.
That said, here is how you can sort such an array:
const arr = ['A (9 km)','B (10 km)','C (2 km)']
arr
.map(point => [pointToNumber(point), point])
.sort(([a], [b]) => {
return a - b;
})
.map(([, point]) => point);
The real magic here is in a - b. This is because a sorting comparator function can return either 0, -1 (or any negative number), or 1 (or any positive number).
As mentioned in the linked msdn, returning a negative number causes a to be sorted into an index lower than b, and returning a positive one causes a to be sorted into an index higher than b.
a - b in a comparator is a shorthand way of writing out that we want to sort in ascending order, because when a > b it will return a positive number so that a moves up, when a < b it returns a negative one so a moves down, and when they're the same it returns zero.
(you could also use b - a if you want descending)

Related

Typescript .sort() skips over a value [duplicate]

This question already has answers here:
How to sort an array of integers correctly
(32 answers)
Closed 6 months ago.
Given the array of numbers
let el = [25000,48000,57000,86000,33000,10000,42000,3000,54000,29000,79000,40000];
The output of the following suggests that it ignores the value of 3000
let el = [25000,48000,57000,86000,33000,10000,42000,3000,54000,29000,79000,40000];
console.log(el.sort());
//Output [10000, 25000, 29000,3000, 33000, 40000,42000, 48000, 54000,57000, 79000, 86000]
So it did sort, but didn't move the 3000 at the 4th position. Why is this happening?
As VLAZ explained, By default, the sort() function sorts values as strings. if numbers are sorted as strings, "25" is bigger than "100", because "2" is bigger than "1".
Because of this, the sort() method will produce incorrect results when sorting numbers.
You can fix this by providing a compare function: (a, b) => a - b)
compareFn(a, b) return value
sort order
> 0
sort a after b
< 0
sort a before b
=== 0
keep original order of a and b
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#description
e.g.
let el = [25000,48000,57000,86000,33000,10000,42000,3000,54000,29000,79000,40000];
console.log(el.sort((a, b) => a - b));

How do you find the 10 indexes of a javascript array that have the highest numeric values?

I am counting the frequency of numbers between 0 and 256 found in a sequence using a javascript array.
If I see the number 74 pop up I am storing it in the 74th index value (i.e. mysequence[74] = mysequence[74] + 1)
I can see the results tallied and its easy when scanning it visually to see that certain numbers (for example 65) has appeared much more often than others. I want to find the 10 numbers that appear most often.
I am concerned a simple sort will not retain the index value which is what I am using to track which number is associated with the counted frequency.
The only thought that comes to mind would be a brute force approach. Creating a function to look through all values one by one keeping the highest and ignoring all indexes that are part of an ignore list. Then running that function 10 times, each subsequent time passing the values of the indexes that have been assigned the top values.
Is there a way to get the numeric keys associated with the top 10 values without resorting to the approach above? (i.e. maybe converting my data to a different format and some fancy map sorting functions?)
You could get the indices and sort them by using the values and get the top wanted indices.
const
values = [7, 3, 4, 5, 2, 8],
indices = [...values.keys()].sort((a, b) => values[b] - values[a]);
console.log(indices.slice(0, 3));
There might be other ways, but what comes to me off the top of my head is to map the original array to a set of value/index pairs, sort the result based on value, then grab the last (or first) 10 items of the sorted array (depending on whether or not you did an ascending or descending sort).
yourArray.map((value, index) => ({ value, index }))
.sort((a, b) => b.value - a.value)
.slice(0, 10)
.map(obj => obj.index);
You can omit the last map if you want to keep both the indices and the values that go with them in your result.
If you just sort it right away, you are definitely going to lose information about the indexes. What you can do is map the array with the values to an array with the index and the value (so that you don't lose information later), then you sort it, but you change the criteria of the sort method in order to get the indexes with top values.
const arr = [2, 1, 0, 3];
const getTopN = (arr, n = 10) => {
const _arr = arr.map((value, index) => [value, index]);
// by using b[0] - a[0] instead of a[0] - b[0] we can get the array in non-increasing order
_arr.sort((a, b) => b[0] - a[0])
return _arr.slice(0, n).map(([_, index]) => index);
}
console.log(getTopN(arr))
The getTopN function does the job.
I would store the values a bit differently (or add a step, where): they are mapped like [number, numberOfAppearances].
Then it's easy to sort them and get the first 10.
const randomIntFromInterval = (min, max) => {
return () => Math.floor(Math.random() * (max - min + 1) + min)
}
const generateRndNum = randomIntFromInterval(0, 100)
const numArr = Array(1000).fill(0).map(_ => generateRndNum())
const tally = numArr.reduce((a, c) => {
if (typeof a[c] === "undefined") a[c] = [c, 0]
a[c][1] += 1
return a
}, [])
const sorted = [...tally].sort(([k1, v1], [k2, v2]) => v2 - v1)
// getting the first ten:
sorted.length = 10
console.log(sorted)

Explanation of .sort in JavaScript [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;
}));

Main difference between map and reduce

I used both methods but I am quite confused regarding the usage of both methods.
Is anything that map can do but reduce can not and vice versa?
Note: I know how to use both methods I am questioning for main difference between these method and when we need to used.
Source
Both map and reduce have as input the array and a function you define. They are in some way complementary: map cannot return one single element for an array of multiple elements, while reduce will always return the accumulator you eventually changed.
map
Using map you iterate the elements, and for each element you return an element you want.
For example, if you have an array of numbers and want to get their squares, you can do this:
// A function which calculates the square
const square = x => x * x
// Use `map` to get the square of each number
console.log([1, 2, 3, 4, 5].map(square))
reduce
Using an array as an input, you can get one single element (let's say an Object, or a Number, or another Array) based on the callback function (the first argument) which gets the accumulator and current_element parameters:
const numbers = [1, 2, 3, 4, 5]
// Calculate the sum
console.log(numbers.reduce(function (acc, current) {
return acc + current
}, 0)) // < Start with 0
// Calculate the product
console.log(numbers.reduce(function (acc, current) {
return acc * current
}, 1)) // < Start with 1
Which one should you choose when you can do the same thing with both? Try to imagine how the code looks. For the example provided, you can compute the squares array like you mentioned, using reduce:
// Using reduce
[1, 2, 3, 4, 5].reduce(function (acc, current) {
acc.push(current*current);
return acc;
}, [])
// Using map
[1, 2, 3, 4, 5].map(x => x * x)
Now, looking at these, obviously the second implementation looks better and it's shorter. Usually you'd choose the cleaner solution, which in this case is map. Of course, you can do it with reduce, but in a nutshell, think which would be shorter and eventually that would be better.
I think this picture will answer you about the difference between those Higher Order Functions
Generally "map" means converting a series of inputs to an equal length series of outputs while "reduce" means converting a series of inputs into a smaller number of outputs.
What people mean by "map-reduce" is usually construed to mean "transform, possibly in parallel, combine serially".
When you "map", you're writing a function that transforms x with f(x) into some new value x1. When you "reduce" you're writing some function g(y) that takes array y and emits array y1.
They produce different results in terms of data structure.
The map() function returns a new array through passing a function over each element in the input array.
This is different to reduce() which takes an array and a function in the same way, but the function takes 2 inputs - an accumulator and a current value.
So reduce() could be used like map() if you always .concat onto the accumulator the next output from a function. However it is more commonly used to reduce the dimensions of an array so either taking a one dimensional and returning a single value or flattening a two dimensional array etc.
Let's take a look of these two one by one.
Map
Map takes a callback and run it against every element on the array but what's
makes it unique is it generate a new array based on your existing array.
var arr = [1, 2, 3];
var mapped = arr.map(function(elem) {
return elem * 10;
})
console.log(mapped); // it genrate new array
Reduce
Reduce method of the array object is used to reduce the array to one single value.
var arr = [1, 2, 3];
var sum = arr.reduce(function(sum, elem){
return sum + elem;
})
console.log(sum) // reduce the array to one single value
I think this question is a very good question and I can't disagree with the answers but I have the feeling we are missing the point entirely.
Thinking of map and reduce more abstractly can provide us with a LOT of very good insights.
This answer is divided in 3 parts:
Defining and deciding between map and reduce (7 minutes)
Using reduce intentionally (8 minutes)
Bridging map and reduce with transducers (5 minutes)
map or reduce
Common traits
map and reduce are implemented in a meaningful and consistent way on a wide range of objects which are not necessarily collections.
They return a value useful to the surrounding algorithm, and they only care about this value.
Their major role is conveying intent regarding transformation or preservation of structure.
Structure
By "structure" I mean a set of conceptual properties which characterise abstract objects, such as an unordered list or a 2D matrix, and their concretion in data structures.
Note that there can be a disconnect between the two:
an unordered list can be stored as an array, which has the concept of ordering carried by indexed keys;
a 2D matrix can be stored as a TypedArray, which lacks the concept of dimension (or nesting).
map
map is a strict structure-preserving transformation.
It is useful to implement it on other kinds of objects to grasp its semantic value:
class A {
constructor (value) {
this.value = value
}
map (f) {
return new A(f(this.value))
}
}
new A(5).map(x => x * 2); // A { value: 10 }
Objects implementing map can have all kinds of behaviours, but they always return the same kind of object you started with while transforming the values with the supplied callback.
Array.map returns an array of the same length and the same ordering as the original.
On the callback arity
Because it preserves structure, map is viewed as a safe operation, but not every callback is equal.
With a unary callback: map(x => f(x)), each value of the array is totally indifferent to the presence of other values.
Using the other two parameters on the other hand introduces coupling, which may not be true to the original structure.
Imagine removing or reordering the second item in the arrays bellow: doing it before or after the map would not yield the same result.
Coupling with array size:
[6, 3, 12].map((x, _, a) => x/a.length);
// [2, 1, 4]
Coupling with ordering:
['foo', 'bar', 'baz'].map((x, i) => [i, x]);
// [[0, 'foo'], [1, 'bar'], [2, 'baz']]
Coupling with one specific value:
[1, 5, 3].map((x, _, a) => x/Math.max(...a));
//[ 0.2, 1, 0.6]
Coupling with neighbours:
const smooth = (x, i, a) => {
const prev = a[i - 1] ?? x;
const next = a[i + 1] ?? x;
const average = (prev + x + next) / 3;
return Math.round((x + average) / 2);
};
[1, 10, 50, 35, 40, 1].map(smoothh);
// [ 3, 15, 41, 38, 33, 8 ] 
I recommend making it explicit on the call site whether or not these parameters are used.
const transfrom = (x, i) => x * i;
❌ array.map(transfrom);
⭕ array.map((x, i) => transfrom(x, i));
This has other benefits when you use variadic functions with map.
❌ ["1", "2", "3"].map(parseInt);
// [1, NaN, NaN]
⭕ ["1", "2", "3"].map(x => parseInt(x));
// [1, 2, 3]
reduce
reduce sets a value free from its surrounding structure.
Again, let's implement it on a simpler object:
class A {
constructor (value) {
this.value = value
}
reduce (f, init) {
return init !== undefined
? f(init, this.value)
: this.value
}
}
new A(5).reduce(); // 5
const concat = (a, b) => a.concat(b);
new A(5).reduce(concat, []); // [ 5 ]
Whether you leave the value alone or you put it back into something else, the output of reduce can be of any shape. It is literally the opposite of map.
Implications for arrays
Arrays can contain multiple or zero values, which gives rise to two, sometimes conflicting, requirements.
The need to combine
How can we return multiple values with no structure around them?
It is impossible. In order to return only one value, we have two options:
summarising the values into one value;
moving the values into a different structure.
Doesn't it make more sense now?
The need to initialise
What if there is no value to return?
If reduce returned a falsy value, there would be no way to know if the source array was empty or if it contained that falsy value, so unless we provide an initial value, reduce has to throw.
The true purpose of the reducer
You should be able to guess what the reducer f does in the following snippet:
[a].reduce(f);
[].reduce(f, a);
Nothing. It is not called.
It is the trivial case: a is the single value we want to return, so f is not needed.
This is by the way the reason why we didn't make the reducer mandatory in our class A earlier: because it contained only one value. It is mandatory on arrays because arrays can contain multiple values.
Since the reducer is only called when you have 2 or more values, saying its sole purpose is to combine them is only a stone throw away.
On transforming values
On arrays of variable lengths, expecting the reducer to transform the values is dangerous because, as we discovered, it may not be called.
I encourage you to map before you reduce when you need to both transform values and change shape.
It is a good idea to keep these two concerns separate for readability anyway.
When not to use reduce
Because reduce is this general-purpose tools for achieving structure transformation, I advise you to avoid it when you want an array back if there exists another more focussed method which does what you want.
Specifically, if you struggle with nested arrays in a map, think of flatMap or flat before reaching for reduce.
At the heart of reduce
a recursive binary operation
Implementing reduce on arrays introduces this feedback loop where the reducer's first argument is the return value of the previous iteration.
Needless to say it looks nothing like map's callback.
We could implement Array.reduce recursively like so:
const reduce = (f, acc, [current, ...rest]) =>
rest.length == 0
? f(acc, current)
: reduce(f, f(acc, current), rest)
This highlights the binary nature of the reducer f and how its return value becomes the new acc in the next iteration.
I let you convince yourself that the following is true:
reduce(f, a, [b, c, d])
// is equivalent to
f(f(f(a, b), c), d)
// or if you squint a little
((a ❋ b) ❋ c) ❋ d
This should seem familiar: you know arithmetic operations obey rules such as "associativity" or "commutativity". What I want to convey here is that the same kind of rules apply.
reduce may strip out the surrounding structure, values are still bound together in an algebraic structure for the time of the transformation.
the algebra of reducers
Algebraic structures are way out of the scope of this answer, so I will only touch on how they are relevant.
((a ❋ b) ❋ c) ❋ d
Looking at the expression above, it is self-evident that there is a constraint that ties all the values together : ❋ must know how to combine them the same way + must know how to combine 1 + 2 and just as importantly (1 + 2) + 3.
Weakest safe structure
One way to ensure this is to enforce that these values belong to a same set on which the reducer is an "internal" or "closed" binary operation, that is to say: combining any two values from this set with the reducer produces a value which belongs to the same set.
In abstract algebra this is called a magma. You can also look up semi-groups which are more talked about and are the same thing with associativity (no braces required), although reduce doesn't care.
Less safe
Living in a magma is not absolutely necessary : we can imagine a situation where ❋ can combine a and b but not c and b.
An example of this is function composition. One of the following functions returns a string, which constrains the order in which you can combine them:
const a = x => x * 2;
const b = x => x ** 2;
const c = x => x + ' !';
// (a ∘ b) ∘ c
const abc = x => c(b(a(x)));
abc(5); // "100 !"
// (a ∘ c) ∘ b
const acb = x => b(c(a(x)));
acb(5); // NaN
Like many binary operations, function composition can be used as a reducer.
Knowing if we are in a situation where reordering or removing elements from an array could make reduce break is kind of valuable.
So, magmas: not absolutely necessary, but very important.
what about the initial value
Say we want to prevent an exception from being thrown when the array is empty, by introducing an initial value:
array.reduce(f, init)
// which is really the same as doing
[init, ...array].reduce(f)
// or
((init ❋ a) ❋ b) ❋ c...
We now have an additional value. No problem.
"No problem"!? We said the purpose of the reducer was to combine the array values, but init is not a true value: it was forcefully introduced by ourselves, it should not affect the result of reduce.
The question is:
What init should we pick so that f(init, a) or init ❋ a returns a?
We want an initial value which acts as though it was not there. We want a neutral element (or "identity").
You can look up unital magmas or monoids (the same with associativity) which are swear words for magmas equipped with a neutral element.
Some neutral elements
You already know a bunch of neutral elements
numbers.reduce((a, b) => a + b, 0)
numbers.reduce((a, b) => a * b, 1)
booleans.reduce((a, b) => a && b, true)
strings.reduce((a, b) => a.concat(b), "")
arrays.reduce((a, b) => a.concat(b), [])
vec2s.reduce(([u,v], [x,y]) => [u+x,v+y], [0,0])
mat2s.reduce(dot, [[1,0],[0,1]])
You can repeat this pattern for many kinds of abstractions. Note that the neutral element and the computation don't need to be this trivial (extreme example).
Neutral element hardships
We have to accept the fact that some reductions are only possible for non-empty arrays and that adding poor initialisers don't fix the problem.
Some examples of reductions gone wrong:
Only partially neutral
numbers.reduce((a, b) => b - a, 0)
// does not work
numbers.reduce((a, b) => a - b, 0)
Subtracting 0 form b returns b, but subtracting b from 0 returns -b.
We say that only "right-identity" is true.
Not every non-commutative operation lack a symmetrical neutral element but it's a good sign.
Out of range
const min = (a, b) => a < b ? a : b;
// Do you really want to return Infinity?
numbers.reduce(min, Infinity)
Infinity is the only initial value which does not change the output of reduce for non-empty arrays, but it is unlikely that we would want it to actually appear in our program.
The neutral element is not some Joker value we add as a convenience. It has to be an allowed value, otherwise it doesn't accomplish anything.
Nonsensical
The reductions bellow rely on position, but adding an initialiser naturally shifts the first element to the second place, which requires messing with the index in the reducer to maintain the behaviour.
const first = (a, b, i) => !i ? b : a;
things.reduce(first, null);
const camelCase = (a, b, i) => a + (
!i ? b : b[0].toUpperCase() + b.slice(1)
);
words.reduce(camelCase, '');
It would have been a lot cleaner to embrace the fact the array can't be empty and simplify the definition of the reducers.
Moreover, the initials values are degenerate:
null is not the first element of an empty array.
an empty string is by no means a valid identifier.
There is no way to preserve the notion of "firstness" with an initial value.
conclusion
Algebraic structures can help us think of our programs in a more systematic way. Knowing which one we are dealing with can predict exactly what we can expect from reduce, so I can only advise you to look them up.
One step further
We have seen how map and reduce were so different structure-wise, but it is not as though they were two isolated things.
We can express map in terms of reduce, because it is always possible to rebuild the same structure we started with.
const map = f => (acc, x) =>
acc.concat(f(x))
;
const double = x => x * 2;
[1, 2, 3].reduce(map(double), []) // [2, 4, 6]
Pushing it a little further has led to neat tricks such as transducers.
I will not go into much detail about them, but I want you to notice a couple of things which will echo what we have said before.
Transducers
First let's see what problem we are trying to solve
[1, 2, 3, 4].filter(x => x % 2 == 0)
.map(x => x ** 2)
.reduce((a, b) => a + b)
// 20
We are iterating 3 times and creating 2 intermediary data structures. This code is declarative, but not efficient. Transducers attempt to reconcile the two.
First a little util for composing functions using reduce, because we are not going to use method chaining:
const composition = (f, g) => x => f(g(x));
const identity = x => x;
const compose = (...functions) =>
functions.reduce(composition, identity)
;
// compose(a, b, c) is the same as x => a(b(c(x)))
Now pay attention to the implementation of map and filter bellow. We are passing in this reducer function instead of concatenating directly.
const map = f => reducer => (acc, x) =>
reducer(acc, f(x))
;
const filter = f => reducer => (acc, x) =>
f(x) ? reducer(acc, x) : acc
;
look at this more specifically:
reducer => (acc, x) => [...]
after the callback function f is applied, we are left with a function which takes a reducer as input and returns a reducer.
These symmetrical functions is what we pass to compose:
const pipeline = compose(
filter(x => x % 2 == 0),
map(x => x ** 2)
);
Remember compose is implemented with reduce: our composition function defined earlier combines our symmetrical functions.
The output of this operation is a function of the same shape: something which expects a reducer and returns a reducer, which means
we have a magma. We can keep composing transformations as long as they have this shape.
we can consume this chain by applying the resulting function with a reducer, which will return a reducer that we can use with reduce
I let you expand the whole thing if you need convincing. If you do so you will notice that transformations will conveniently be applied left to right, which is the opposite direction of compose.
Alright, lets use this weirdo:
const add = (a, b) => a + b;
const reducer = pipeline(add);
const identity = 0;
[1, 2, 3, 4].reduce(reducer, identity); // 20
We have composed operations as diverse as map, filter and reduce into a single reduce, iterating only once with no intermediary data-structure.
This is no small achievement! And it is not a scheme you can come up with by deciding between map and reduce merely on the basis of the conciseness of the syntax.
Also notice that we have full control over the initial value and the final reducer. We used 0 and add, but we could have used [] and concat (more realistically push performance-wise) or any other data-structure for which we can implement a concat-like operation.
To understand the difference between map, filter and reduce, remember this:
All three methods are applied on array so anytime you want to make any operation on an array, you will be using these methods.
All three follow functional approaches and therefore the original array remains the same. Original array doesn't change instead a new array/value is returned.
Map returns a new array with the equal no. of elements as there are in the original array. Therefore, if the original array has 5 elements, the returned array will also have 5 elements. This method is used whenever we want to make some change on every individual element of an array. You can remember that every element of ann array is being mapped to some new value in output array, therefore the name map
For eg,
var originalArr = [1,2,3,4]
//[1,2,3,4]
var squaredArr = originalArr.map(function(elem){
return Math.pow(elem,2);
});
//[1,4,9,16]
Filter returns a new array with equal/less number of elements than the original array. It returns those elements in the array which have passed some condition. This method is used when we want to apply a filter on the original array therefore the name filter. For eg,
var originalArr = [1,2,3,4]
//[1,2,3,4]
var evenArr = originalArr.filter(function(elem){
return elem%2==0;
})
//[2,4]
Reduce returns a single value, unlike a map/filter. Therefore, whenever we want to run an operation on all elements of an array but want a single output using all elements, we use reduce. You can remember an array's output is reduced to a single value therefore the name reduce. For eg,
var originalArr = [1,2,3,4]
//[1,2,3,4]
var sum = originalArr.reduce(function(total,elem){
return total+elem;
},0)
//10
The map function executes a given function on each element but reduce executes a function which reduces the array to a single value. I'll give an example of both:
// map function
var arr = [1, 2, 3, 4];
var mappedArr = arr.map((element) => { // [10, 20, 30, 40]
return element * 10;
})
// reduce function
var arr2 = [1, 2, 3, 4]
var sumOfArr2 = arr2.reduce((total, element) => { // 10
return total + element;
})
It is true that reduce reduces an array to a single value, but since we can pass an object as initialValue, we can build upon it and end up with a more complex object than what we started with, such as this example where we group items by some criteria. Therefore the term 'reduce' can be slightly misleading as to the capabilities of reduce and thinking of it as necessarily reducing information can be wrong since it could also add information.
let a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let b = a.reduce((prev, curr) => {
if (!prev["divisibleBy2"]) {
prev["divisibleBy2"] = []
}
if (curr % 2 === 0) {
prev["divisibleBy2"].push(curr)
}
if (!prev["divisibleBy3"]) {
prev["divisibleBy3"] = []
}
if (curr % 3 === 0) {
prev["divisibleBy3"].push(curr)
}
if (!prev["divisibleBy5"]) {
prev["divisibleBy5"] = []
}
if (curr % 5 === 0) {
prev["divisibleBy5"].push(curr)
}
return prev
}, {})
console.log(b)

What is the default sort for 2D arrays?

when using Array.sort()
I would assume it takes the first index but I wanted to verify.
MDN reference did not say anything:
This SO Post is not relevant.
For this method: ( sorting off the first index will work fine ).
$P.aZindex = function () {
var arr_2d = {},
elements = document.body.getElementsByTagName("*"),
element,
length,
z_index;
// loop through elements and pull information from them
$A.eachIndex(elements, function(val, index){
z_index = win.getComputedStyle(val).getPropertyValue("z-index");
// ignore elements with the auto value
if (z_index !== "auto") {
arr_2d[i] = [val.id, val.tagName, val.className, z_index];
}
});
// sort the array
arr_2d.sort();
return arr_2d;
};
Let's examine the EMCAScript specification for the array sort method. The sort algorithm iteratively compares pairs of elements from the array; those comparisons yield either -1, 1, or 0 (for less than, greater than, or equal to, respectively), and the result of each comparison is used to build a sorted array.
In particular, we are concerned with the "default" comparison case of sort, in which no comparison function is specified. When comparing some pair of x and y:
Let xString be ToString(x).
Let yString be ToString(y).
If xString < yString, return −1.
If xString > yString, return 1.
Return +0.
ECMAScript's ToString, when applied to objects, calls the object's toString method.
To clarify what is meant by x > y and x < y, we can examine ECMAScript's Abstract Relational Comparison Algorithm, which specifies the behavior of the > and < operators. In the event that the operands px and py are both strings:
Let k be the smallest nonnegative integer such that the character at position k within px is different from the character at position k within py...
Let m be the integer that is the code unit value for the character at position k within px.
Let n be the integer that is the code unit value for the character at position k within py.
If m < n, return true. Otherwise, return false.
This is a simple string comparison, based on a comparison of Unicode code unit values of the character at the first differing position within the strings.
As you can see, the sort algorithm stringifies each object element (using the element's toString method) that it contains and then compares those strings to determine ordering. I understand that this seems strange to you: if you have nothing but array elements in your sorting array, why not use the elements of those subarrays to determine ordering? This is simply because the EMCAScript specification chose to keep default element comparison of a potentially heterogeneous array extremely general, since any type of element can be rendered as a string.
However, if array-descent behavior is what you want, it's possible to implement:
var compare_items = function(a,b) {
var result;
if(a instanceof Array && b instanceof Array) {
// iteratively compare items from each array
for(var i=0; i<Math.min(a.length,b.length); ++i) {
result = compare_items(a[i], b[i]);
if(result != 0) { return result; }
}
// if both arrays are equal so far, length is the determining factor
return a.length - b.length;
}
// if the items are both numbers, report their numeric relation
if(typeof a == "number" && typeof b == "number") {
return a - b;
}
// otherwise, fall back to strings
if(a.toString() == b.toString()) { return 0; }
return a.toString() > b.toString() ? 1 : -1;
}
Then, use this comparator function like arr_2d.sort(compare_items);.
This allows you to sort arbitrarily deep N-dimensional arrays. First we compare a[0]...[0][0] against b[0]...[0][0] then a[0]...[0][1] to b[0]...[0][1]; then, if the [0]...[0][*] subarray proves equal, we move up to a[0]...[1][0], etc. Comparing arrays of differing dimension may produce unusual results, because a non-array may be compared to an array, which compares the stringified forms of each operand.
Note that this function has strange results if you have a heterogeneous array: [1, 2, 3, [1,2], [2,3]] sorts to [1, [1,2], 2, [2,3], 3]. The arrays and non-array are relatively sorted correctly, but the arrays are scattered in with the non-arrays in a non-intuitive way.
From the documentation you link to:
the array is sorted lexicographically (in dictionary order) according to the string conversion of each element
This doesn't change just because those elements are also arrays.

Categories

Resources