Как мне развалиться? Может быть, монады в святилище?
Вот простое цепочечное выражение, использующее современный javascript для поиска значения для конкретного ключа, расположенного в строке, содержащей разделенный запятыми список пар ключ-значение, разделенных =
,
Это падает, если источник нулевой или ключ не найден, в моей голове это казалось большой задачей для монады Maybe.
// Grab the tag with key in `tag`
const getTag = (product, tag) =>
product.Tags
.split(',')
.find(t => t.startsWith(`${tag}=`))
.split('=')[1]
getTag({Tags: 'a=y,b=z'}, 'a') // returns 'y'
getTag({Tags: 'a=y,b=z'}, 'z') // returns boom (desired null)
getTag({Tags: null}, 'a') // returns boom (desired null)
Поэтому я npm установил убежище и начал играть с функциональным решением. Это так далеко, как я дошел до этого, и гонорар, как будто это довольно уродливо, это говорит мне о том, что я должен что-то делать неправильно или использовать неправильные инструменты.
const getk = S.map(S.filter(S.test(/=/)))(S.splitOn(','))
S.map(S.map(S.map(S.splitOn('='))))(S.map(getk))(S.toMaybe(null))
// Nothing
S.map(S.map(S.map(S.splitOn('='))))(S.map(getk))(S.toMaybe('a=y,b=z'))
//Just ([["a", "y"], ["b", "z"]])
Я не хотел, чтобы это был вопрос "решить эту проблему для меня", но мне трудно передать, что именно мне действительно нужно для помощи.
NB. Я все еще пытаюсь "выяснить" FP, так что это определенно проблема знакомства.
1 ответ
Мы можем использовать S.map
преобразовать внутренние ценности и S.join
удалить нежелательную вложенность:
const S = require ('sanctuary');
const $ = require ('sanctuary-def');
// getTag :: String -> Object -> Maybe String
const getTag = tag => S.pipe ([
S.get (S.is ($.String)) ('Tags'), // :: Maybe String
S.map (S.splitOn (',')), // :: Maybe (Array String)
S.map (S.map (S.stripPrefix (tag + '='))), // :: Maybe (Array (Maybe String))
S.map (S.head), // :: Maybe (Maybe (Maybe String))
S.join, // :: Maybe (Maybe String)
S.join, // :: Maybe String
]);
getTag ('a') ({Tags: 'a=y,b=z'}); // => Just ('y')
getTag ('z') ({Tags: 'a=y,b=z'}); // => Nothing
getTag ('z') ({Tags: null}); // => Nothing
S.map
с последующим S.join
всегда эквивалентно S.chain
:
// getTag :: String -> Object -> Maybe String
const getTag = tag => S.pipe ([
S.get (S.is ($.String)) ('Tags'), // :: Maybe String
S.map (S.splitOn (',')), // :: Maybe (Array String)
S.map (S.map (S.stripPrefix (tag + '='))), // :: Maybe (Array (Maybe String))
S.chain (S.head), // :: Maybe (Maybe String)
S.join, // :: Maybe String
]);
Этот подход делает немного ненужной работы, не закорачивая, но S.stripPrefix
позволяет нам за один шаг проверить, существует ли тег, и извлечь его значение, если оно есть.:)
Обновленная версия, которая использует S.justs
выбрать первое совпадение:
// getTag :: String -> Object -> Maybe String
const getTag = tag => S.pipe ([
S.get (S.is ($.String)) ('Tags'), // :: Maybe String
S.map (S.splitOn (',')), // :: Maybe (Array String)
S.map (S.map (S.stripPrefix (tag + '='))), // :: Maybe (Array (Maybe String))
S.map (S.justs), // :: Maybe (Array String)
S.chain (S.head), // :: Maybe String
]);
Вот альтернатива коду Sanctuary, использующая только современный JavaScript:
const stripPrefix = e =>
e.startsWith(`${tag}=`)
&& e.replace(`${tag}=`, "")
const getTag = tag => product =>
product?.Tags
?.split(",")
.map(stripPrefix)
.filter(Boolean)
[0]
|| null
getTag("b")({ Tags: "a=y,b=c" }) // returns 'y'
getTag("z")({ Tags: "a=y,b=z" }) // returns null
getTag("a")({ Tags: null }) // returns null
В stripPrefix
функция возвращает false
если тег не найден, он получает filter
изд.
И ты можешь справиться с { Tags: null }
с помощью необязательного оператора цепочки (?.
).