In my project, I need to sort an array that contain index of an other array (it\'s item). I\'ve search for many hours, but I did not find anyone with my problem.
<
functional arbitrary sort
Here's another way to approach your problem. Let's say we have some fruits
and an arbitrary order
we wish to sort them in -
const fruits =
// 0 1 2 3 4
[ "apple", "banana", "cherry", "orange", "peach" ]
const order =
[ 1, 3, 2, 0, 4 ]
We want to be able to write something like this -
fruits.sort(sortByIndex(fruits, order))
console.log(fruits)
// [ "banana", "orange", "cherry", "apple", "peach" ]
// 1 3 2 0 4
We wish up a Comparison
module to handle our sorting code -
const { empty, map } =
Comparison
const sortByIndex = (values = [], indexes = []) =>
map(empty, x => indexes.indexOf(values.indexOf(x)))
Now we just have to implement Comparison
-
const Comparison =
{ empty: (a, b) =>
a < b ? -1
: a > b ? 1
: 0
, map: (m, f) =>
(a, b) => m(f(a), f(b))
}
const { empty, map } =
Comparison
const sortByIndex = (values = [], indexes = []) =>
map(empty, x => indexes.indexOf(values.indexOf(x)))
const fruits =
[ "apple", "banana", "cherry", "orange", "peach" ]
// 0 1 2 3 4
const order =
[ 1, 3, 2, 0, 4 ]
console.log(fruits)
// [ "apple", "banana", "cherry", "orange", "peach" ]
console.log(fruits.sort(sortByIndex(fruits, order)))
// [ "banana", "orange", "cherry", "apple", "peach" ]
why a module?
Implementing a Comparison
module means we have a tidy place to store all of our comparison logic. We could easily implement other useful functions like reverse
and concat
now -
const Comparison =
{ // ...
, concat: (m, n) =>
(a, b) => Ordered.concat(m(a, b), n(a, b))
, reverse: (m) =>
(a, b) => m(b, a)
}
const Ordered =
{ empty: 0
, concat: (a, b) =>
a === 0 ? b : a
}
Now we can model complex sorting logic with ease -
const sortByName =
map(empty, x => x.name)
const sortByAge =
map(empty, x => x.age)
const data =
[ { name: 'Alicia', age: 10 }
, { name: 'Alice', age: 15 }
, { name: 'Alice', age: 10 }
, { name: 'Alice', age: 16 }
]
Sort by name
then sort by age
-
data.sort(concat(sortByName, sortByAge))
// [ { name: 'Alice', age: 10 }
// , { name: 'Alice', age: 15 }
// , { name: 'Alice', age: 16 }
// , { name: 'Alicia', age: 10 }
// ]
Sort by age
then sort by name
-
data.sort(concat(sortByAge, sortByName))
// [ { name: 'Alice', age: 10 }
// , { name: 'Alicia', age: 10 }
// , { name: 'Alice', age: 15 }
// , { name: 'Alice', age: 16 }
// ]
And effortlessly reverse
any sorter. Here we sort by name
then reverse sort by age
-
data.sort(concat(sortByName, reverse(sortByAge)))
// [ { name: 'Alice', age: 16 }
// , { name: 'Alice', age: 15 }
// , { name: 'Alice', age: 10 }
// , { name: 'Alicia', age: 10 }
// ]
functional principles
Our Comparison
module is flexible yet reliable. This allows us to write our sorters in a formula-like way -
// this...
concat(reverse(sortByName), reverse(sortByAge))
// is the same as...
reverse(concat(sortByName, sortByAge))
And similarly with concat
expressions -
// this...
concat(sortByYear, concat(sortByMonth, sortByDay))
// is the same as...
concat(concat(sortByYear, sortByMonth), sortByDay)
// is the same as...
nsort(sortByYear, sortByMonth, sortByDay)
go nuts with nsort
Now let's say we want to sort by an arbitrary number of factors. For example, sorting date objects requires three comparisons: year
, month
, and day
-
const { empty, map, reverse, nsort } =
Comparison
const data =
[ { year: 2020, month: 4, day: 5 }
, { year: 2018, month: 1, day: 20 }
, { year: 2019, month: 3, day: 14 }
]
const sortByDate =
nsort
( map(empty, x => x.year) // primary: sort by year
, map(empty, x => x.month) // secondary: sort by month
, map(empty, x => x.day) // tertiary: sort by day
)
Now we can sort by year
, month
, day
-
data.sort(sortByDate)
// [ { year: 2019, month: 11, day: 14 }
// , { year: 2020, month: 4, day: 3 }
// , { year: 2020, month: 4, day: 5 }
// ]
And just as easily reverse sort by year
, month
, day
-
data.sort(reverse(sortByDate))
// [ { year: 2020, month: 4, day: 5 }
// , { year: 2020, month: 4, day: 3 }
// , { year: 2019, month: 11, day: 14 }
// ]
Implementing N-sort is a breeze thanks to functional principles. Our concat
and empty
do all the hard work -
const Comparison =
{ // ...
, nsort: (...m) =>
m.reduce(Comparison.concat, Comparison.empty)
}
Expand the snippet below to see this code in action -
const Comparison =
{ empty: (a, b) =>
a < b ? -1
: a > b ? 1
: 0
, map: (m, f) =>
(a, b) => m(f(a), f(b))
, concat: (m, n) =>
(a, b) => Ordered.concat(m(a, b), n(a, b))
, reverse: (m) =>
(a, b) => m(b, a)
, nsort: (...m) =>
m.reduce(Comparison.concat, Comparison.empty)
}
const Ordered =
{ empty: 0
, concat: (a, b) =>
a === 0 ? b : a
}
const { empty, map, concat, reverse, nsort } =
Comparison
const sortByDate =
nsort
( map(empty, x => x.year) // primary
, map(empty, x => x.month) // secondary
, map(empty, x => x.day) // tertiary
)
const data =
[ { year: 2020, month: 4, day: 5 }
, { year: 2019, month: 11, day: 14 }
, { year: 2020, month: 4, day: 3 }
]
console.log(data.sort(reverse(sortByDate)))
// [ { year: 2020, month: 4, day: 5 }
// , { year: 2020, month: 4, day: 3 }
// , { year: 2019, month: 11, day: 14 }
// ]
JavaScript modules
Above Comparison
and Ordered
are defined as simple objects. JavaScript is a very flexible language and import
/export
syntaxes were made explicitly available for modularising your programs. Writing modules in this way gives us a clear picture of where things should go and provides us with plenty of room to grow our code -
// Comparison.js
import { lt, gt, eq, concat:_concat } from "./Ordered"
const asc = (a, b) =>
(console.log(a, b), a < b) ? lt
: a > b ? gt
: eq
const empty =
asc
const map = (m, f) =>
(a, b) => m(f(a), f(b))
const concat = (m, n) =>
(a, b) => _concat(m(a, b), n(a, b))
const reverse = (m) =>
(a, b) => m(b, a)
const desc =
reverse(asc)
export { asc, concat, desc, empty, map, reverse }
// Ordered.js
const lt =
-1
const gt =
1
const eq =
0
const empty =
eq
const concat = (a, b) =>
a === eq ? b : a
export { concat, empty, eq, gt, lt }
Much Code, but it works :)
For sort ASC:
var test = [1, 4, 3, 4, 5, 6, 7, 8, 9];
console.log("Original Array: " + test);
var len = test.length;
var indices = new Array(len);
for (var i = 0; i < len; ++i) indices[i] = i;
indices.sort(function (a, b) { return test[a] < test[b] ? -1 : test[a] > test[b] ? 1 : 0; });
test.sort();
console.log("Sort-ASC " + test);
console.log("Index from Array " + indices);
For sort DESC:
var test = [1, 4, 3, 4, 5, 6, 7, 8, 9];
console.log("Originales Array: " + test)
var len = test.length;
var indices = new Array(len);
for (var i = 0; i < len; ++i) indices[i] = i;
indices.sort(function (a, b) { return test[a] < test[b] ? -1 : test[a] > test[b] ? 1 : 0; });
indices.reverse();
test.sort();
test.reverse();
console.log("Sort-DESC " + test);
console.log("Index from Array " + indices);
Its much easy buddy use this arr.sort(function(a, b){return a - b}); // use return otherwise it will not triggered and - + plus denote ascndng dcndng ordee Simple method ..... Happy coding
You need to return the delta. Otherwise the callback returns undefined
for each call.
arr2.sort(function(a, b) {
return arr[b] - arr[a];
});
For adding the right order, you need ot take the index from indices
to address the right element and assign i
as style order value.
function sort() {
var array = [1, 4, 3, 4, 5, 6, 7, 8, 9],
z = document.getElementsByClassName("triable");
[...array.keys()]
.sort((a, b) => array[b] - array[a])
.forEach((v, i) => z[v].style.order = i);
}
<button onclick="sort()">sort</button><br>
<div style="display: flex;">
<span class="triable">1</span>
<span class="triable">4</span>
<span class="triable">3</span>
<span class="triable">4</span>
<span class="triable">5</span>
<span class="triable">6</span>
<span class="triable">7</span>
<span class="triable">8</span>
<span class="triable">9</span>
</div>