Загальні процедури означають, що нам не потрібно переписувати складність щоразу, коли нам потрібно використовувати певну поведінку.
concatMap
(або flatMap
) саме те, що нам потрібно в цій ситуації.
// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
xs.concat (ys)
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
xs.map(f).reduce(concat, [])
// id :: a -> a
const id = x =>
x
// flatten :: [[a]] -> [a]
const flatten =
concatMap (id)
// your sample data
const data =
[["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]
console.log (flatten (data))
передбачення
І так, ви правильно це здогадалися, це лише згладжує один рівень, саме так воно має працювати
Уявіть собі такий набір даних
// Player :: (String, Number) -> Player
const Player = (name,number) =>
[ name, number ]
// team :: ( . Player) -> Team
const Team = (...players) =>
players
// Game :: (Team, Team) -> Game
const Game = (teamA, teamB) =>
[ teamA, teamB ]
// sample data
const teamA =
Team (Player ('bob', 5), Player ('alice', 6))
const teamB =
Team (Player ('ricky', 4), Player ('julian', 2))
const game =
Game (teamA, teamB)
console.log (game)
// [ [ [ 'bob', 5 ], [ 'alice', 6 ] ],
// [ [ 'ricky', 4 ], [ 'julian', 2 ] ] ]
Гаразд, скажіть, що ми хочемо надрукувати реєстр, який показує всіх гравців, які братимуть участь у game
...
const gamePlayers = game =>
flatten (game)
gamePlayers (game)
// => [ [ 'bob', 5 ], [ 'alice', 6 ], [ 'ricky', 4 ], [ 'julian', 2 ] ]
Якщо наша flatten
процедура також згладила вкладені масиви, ми б закінчилися з цим результатом сміття…
const gamePlayers = game =>
badGenericFlatten(game)
gamePlayers (game)
// => [ 'bob', 5, 'alice', 6, 'ricky', 4, 'julian', 2 ]
котиться глибоко, дитино
Це не означає, що іноді ви також не хочете розгладжувати вкладені масиви - тільки це не повинно бути поведінкою за замовчуванням.
Ми можемо зробити deepFlatten
процедуру з легкістю ...
// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
xs.concat (ys)
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
xs.map(f).reduce(concat, [])
// id :: a -> a
const id = x =>
x
// flatten :: [[a]] -> [a]
const flatten =
concatMap (id)
// deepFlatten :: [[a]] -> [a]
const deepFlatten =
concatMap (x =>
Array.isArray (x) ? deepFlatten (x) : x)
// your sample data
const data =
[0, [1, [2, [3, [4, 5], 6]]], [7, [8]], 9]
console.log (flatten (data))
// [ 0, 1, [ 2, [ 3, [ 4, 5 ], 6 ] ], 7, [ 8 ], 9 ]
console.log (deepFlatten (data))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Там. Тепер у вас є інструмент для кожної роботи - один для збивання одного рівня гніздування flatten
, і один для знищення всіх гнізд deepFlatten
.
Можливо, ви можете зателефонувати йому, obliterate
або nuke
якщо ім'я вам не сподобається deepFlatten
.
Не повторюйте двічі!
Звичайно, вищезазначені реалізації є розумними та стислими, але використання .map
наступного дзвінка .reduce
означає, що ми робимо більше ітерацій, ніж потрібно
Використання надійного комбінатора, який я телефоную, mapReduce
допомагає зберегти ітерації до мінімуму; вона бере функцію відображення, функцію m :: a -> b
відновлення r :: (b,a) ->b
і повертає нову функцію відновлення - цей комбінатор лежить в основі перетворювачів ; якщо вас цікавить, я написав інші відповіді на них
// mapReduce = (a -> b, (b,a) -> b, (b,a) -> b)
const mapReduce = (m,r) =>
(acc,x) => r (acc, m (x))
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
xs.reduce (mapReduce (f, concat), [])
// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
xs.concat (ys)
// id :: a -> a
const id = x =>
x
// flatten :: [[a]] -> [a]
const flatten =
concatMap (id)
// deepFlatten :: [[a]] -> [a]
const deepFlatten =
concatMap (x =>
Array.isArray (x) ? deepFlatten (x) : x)
// your sample data
const data =
[ [ [ 1, 2 ],
[ 3, 4 ] ],
[ [ 5, 6 ],
[ 7, 8 ] ] ]
console.log (flatten (data))
// [ [ 1. 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ] ]
console.log (deepFlatten (data))
// [ 1, 2, 3, 4, 5, 6, 7, 8 ]