4

这是一个简单的链式表达式,使用现代 javascript 来查找位于字符串中的特定键的值,该字符串包含逗号分隔的键值对列表,由=.

如果源为空或未找到密钥,这将失败,在我看来,这对于 Maybe monad 来说似乎是一项伟大的任务。

// 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 安装了 sanctuary 并开始使用功能解决方案。这是迄今为止我所获得的,并且觉得它很丑陋,这告诉我我一定做错了什么或使用了错误的工具。

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"]])

我不希望这是一个“为我解决这个问题”的问题,但我很难传达我真正需要帮助的地方。

注意我仍在尝试“弄清楚” FP,所以这绝对是一个熟悉的问题。

4

2 回答 2

4

我们可以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
]);
于 2018-08-11T02:04:39.237 回答
1

这是仅使用现代 JavaScript 的 Sanctuary 代码的替代方案:

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函数返回,然后被编辑。falsefilter

您可以{ Tags: null }使用可选的链接运算符 ( ?.) 来处理。

于 2019-12-17T02:50:43.297 回答