функциональная произвольная сортировка
Вот еще один способ решения вашей проблемы. Допустим, у нас есть некоторые fruits
и произвольные order
мы будем sh для их сортировки -
const fruits =
// 0 1 2 3 4
[ "apple", "banana", "cherry", "orange", "peach" ]
const order =
[ 1, 3, 2, 0, 4 ]
Мы хотим иметь возможность написать что-то вроде этого -
fruits.sort(sortByIndex(fruits, order))
console.log(fruits)
// [ "banana", "orange", "cherry", "apple", "peach" ]
// 1 3 2 0 4
Мы обработаем sh модуль Comparison
для обработки нашего кода сортировки -
const { empty, contramap } =
Comparison
const sortByIndex = (values = [], indexes = []) =>
contramap(empty, x => indexes.indexOf(values.indexOf(x)))
Теперь нам просто нужно реализовать Comparison
-
const Comparison =
{ empty: (a, b) =>
a < b ? -1
: a > b ? 1
: 0
, contramap: (m, f) =>
(a, b) => m(f(a), f(b))
}
const { empty, contramap } =
Comparison
const sortByIndex = (values = [], indexes = []) =>
contramap(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" ]
почему модуль?
Реализация модуля Comparison
означает, что у нас есть аккуратное место для хранения всех наше сравнение логи c. Мы могли бы легко реализовать другие полезные функции, такие как reverse
и concat
сейчас -
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
}
Теперь мы можем легко смоделировать сложные алгоритмы сортировки c -
const sortByName =
contramap(empty, x => x.name)
const sortByAge =
contramap(empty, x => x.age)
const data =
[ { name: 'Alicia', age: 10 }
, { name: 'Alice', age: 15 }
, { name: 'Alice', age: 10 }
, { name: 'Alice', age: 16 }
]
Сортировать по name
затем сортируйте по age
-
data.sort(concat(sortByName, sortByAge))
// [ { name: 'Alice', age: 10 }
// , { name: 'Alice', age: 15 }
// , { name: 'Alice', age: 16 }
// , { name: 'Alicia', age: 10 }
// ]
Сортируйте по age
, затем сортируйте по name
-
data.sort(concat(sortByAge, sortByName))
// [ { name: 'Alice', age: 10 }
// , { name: 'Alicia', age: 10 }
// , { name: 'Alice', age: 15 }
// , { name: 'Alice', age: 16 }
// ]
И без усилий reverse
любой сортировщик. Здесь мы сортируем по name
, затем выполняем обратную сортировку по age
-
data.sort(concat(sortByName, reverse(sortByAge)))
// [ { name: 'Alice', age: 16 }
// , { name: 'Alice', age: 15 }
// , { name: 'Alice', age: 10 }
// , { name: 'Alicia', age: 10 }
// ]
принципам работы
Наш Comparison
модуль является гибким, но надежным. Это позволяет нам писать наши сортировщики по формуле -
// this...
concat(reverse(sortByName), reverse(sortByAge))
// is the same as...
reverse(concat(sortByName, sortByAge))
И аналогично с concat
выражениями -
// this...
concat(sortByYear, concat(sortByMonth, sortByDay))
// is the same as...
concat(concat(sortByYear, sortByMonth), sortByDay)
// is the same as...
nsort(sortByYear, sortByMonth, sortByDay)
go гайки с nsort
Теперь предположим, что мы хотим отсортировать по произвольному числу факторов. Например, для сортировки объектов даты требуется три сравнения: year
, month
и day
-
const { empty, contramap, 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
( contramap(empty, x => x.year) // primary: sort by year
, contramap(empty, x => x.month) // secondary: sort by month
, contramap(empty, x => x.day) // tertiary: sort by day
)
Теперь мы можем сортировать по year
, month
, day
-
data.sort(sortByDate)
// [ { year: 2019, month: 11, day: 14 }
// , { year: 2020, month: 4, day: 3 }
// , { year: 2020, month: 4, day: 5 }
// ]
И так же легко выполнить обратную сортировку по 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 }
// ]
Реализация N-сортировки очень проста благодаря функциональным принципам. Наши concat
и empty
выполняют всю тяжелую работу -
const Comparison =
{ // ...
, nsort: (...m) =>
m.reduce(Comparison.concat, Comparison.empty)
}
Разверните фрагмент ниже, чтобы увидеть этот код в действии -
const Comparison =
{ empty: (a, b) =>
a < b ? -1
: a > b ? 1
: 0
, contramap: (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, contramap, concat, reverse, nsort } =
Comparison
const sortByDate =
nsort
( contramap(empty, x => x.year) // primary
, contramap(empty, x => x.month) // secondary
, contramap(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 }
// ]