簡體   English   中英

在 Ramda 中有條件地根據另一個數組/字符串過濾一個數組

[英]Filter an array against another array/string conditionally in Ramda

我正在練習 Ramda,並嘗試構建一個 function 如下:

function 需要兩個參數:

userInput: {
  query: Array[String] || String,
  target: Array[String]
}

目標:

  • 過濾目標數組並返回滿足特定條件的新數組
  • 作為新數組的條件應該只包含以給定查詢開頭的字符串。

例如:如果目標是:

["pen", "pencil", "paper", "", undefined, True, "books", "paperback"]

查詢是:

["pen", "paper"]

那么過濾后的結果應該是:

["pen", "pencil", "paper", "paperback"]

我已經以普通/香草(?)js方式實現了目標。 但這不一定是 FP,也不是利用 Ramda。

到目前為止,我的實驗是這樣的:

  • 迭代查詢數組(如果它是一個數組,或者只是純字符串),並針對每個查詢檢查目標(使用 Ramda 的startsWith );
  • 返回真/假目標字符串是否滿足任何條件(使用來自 Ramda 的anyanyPass );
  • 根據上述步驟中的 T/F 值過濾整個數組;

在代碼方面,我正在考慮使用mapapply startsWith function 應用於目標數組的每個元素。 到目前為止,我只這樣做了:

const textStartsWith = curry((query, target) =>
  pipe(toString, startsWith(query))(target)
);

但是,我被困在這里對函數的組合進行了柯里化。

任何幫助將非常感激!

我會像這樣結合startsWithanyPass

 const textStartsWith = pipe ( map (startsWith), anyPass, flip (o) (String), filter ) console.log ( textStartsWith (['pen', 'paper']) (['pen', 'pencil', 'paper', '', undefined, true, 'books', 'paperback']) )
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script> <script>const {pipe, map, startsWith, anyPass, flip, o, filter} = R </script>

如果您希望能夠在一個 go 中通過 arguments,您可以將其包裝在uncurry中:

const textStartsWith = uncurryN (2) (pipe (
  map (startsWith), 
  anyPass,
  flip (o) (String),
  filter
))

textStartsWith (query, target)

我認為這確實指出了 Ramda 中缺少的 function。 Ramda 具有可變參數composepipe函數,以及 curried 二進制 compose, o 但是沒有等效的咖喱二進制pipe

如果您閱讀 Haskell

One possible way to arrive at such an implementation is to make a fully curried function, and then paste a Haskell equivalent into http://pointfree.io .

所以如果我們從這個 function 開始:

const f1 = (query) => (target) => filter (pipe (
  String, 
  anyPass (map( startsWith) (query))
)) (target)

我們可以像這樣制作 Haskell 版本:

\query -> \target -> filter ((anyPass ((map startsWith) query)) . string) target

然后返回:

filter . (. string) . anyPass . map startsWith

我們可以像上面第一個答案一樣通過注意foo. bar foo. barfoobar的組合,而(. foo)等價於flip (o) (foo)o (__, foo)

我們最終可以得到類似於上面第一個片段的內容。

更新

用戶 Kuncheria 詢問了flip (o) (String) 也許瀏覽簽名可能會有所幫助。 我們將四個函數傳遞給 pipe。

map (startsWith)具有簽名[String] -> [(String -> Boolean)] 它接受一個字符串列表並將函數列表從字符串返回到 Boolean。

anyPass具有簽名[(a -> Boolean)] -> (a -> Boolean) 它從某個任意類型( aBoolean )獲取函數列表,並從a到 Boolean 返回單個Boolean (對於提供的函數中的a ,這將是true的)。

Now we can combine the output of map (startsWith) ( [(String -> Boolean)] with the input to anyPass , by substituting String for a , and so pipe (map (startsWith), anyPass)) has the signature [String] -> (String -> Boolean)

flip (o) (String)這里是最復雜的function,下面我們會解釋。 在那里我們會發現它的類型是(String -> c) -> (a -> c)

And now substituting Boolean for c , we combine with the above to to see that pipe (map (startsWith), anyPass, flip (o) (String)) has the signature [String] -> (a -> Boolean) .

filter僅具有簽名(a -> Boolean) -> [a] -> [a] It accepts a function that transforms a value of type a into a boolean, and returns a function that takes a list of values of type a and returns the filtered list of those for which the function returns true .

所以結合上面的,我們可以注意到我們的主要 function -- pipe (map (startsWith), anyPass, flip (o) (String), filter) -- 有簽名[String] -> [a] -> [a]

我們可以把上面的討論寫得更簡潔:

const textStartsWith = pipe (
  map (startsWith),    // [String] -> [(String -> Boolean)]
  anyPass,             // [(a -> Boolean)] -> (a -> Boolean)
     // a = String  =>    [String] -> (String -> Boolean)
  flip (o) (String),   // (String -> c) -> (a -> c)
     // c = Boolean =>    [String] -> (a -> Boolean) 
  filter               // (a -> Boolean) -> [a] -> [a]
     //             =>    [String] -> [a] -> [a]
)

但是我們仍然需要討論flip (o) (String)

o是一個 curried 二進制compose function,其簽名是

o :: (b -> c) -> (a -> b) -> (a -> c)

我們可以flip它,得到:

flip (o) :: (a -> b) -> (b -> c) -> (a -> c)

現在我們遇到了一個符號問題。 我們一直在使用String來表示 String 類型。 但是在 JS 中, String也是一個 function:用任何值構造一個 String。 我們可以將其視為從某種類型a到字符串的 function,即類型a -> String 所以,既然

flip (o) :: (a -> b) -> (b -> c) -> (a -> c)

我們可以看到:

flip (o) (String)
;            ^----------------- Constructor function
flip (o) (a -> String)
;                 ^------------ Data type
flip (o) (String) :: (String -> c) -> (a -> c)
;            ^           ^----- Data type
;            +----------------- Constructor function

We can think of flip (o) (String) as a function that accepts a function which transforms a String into type c , and returns a function which transforms something of type a into something of type c . 一個例子是length , function 采用字符串的長度:

const strLength = flip (o) (String) (length)
strLength ('abc')  //=> 3  because String ('abc') = 'abc'
strLength (42)     //=> 2  because String (42) = '42'
strLength (void 0) //=> 9  because String (void 0) = 'undefined'
strLength ({})     //=> 15 because String ({}) = 'object [Object]'

如果query不是數組,則將其轉換為數組(請參閱convertToArray )。 Map query ,並使用R.startsWith創建一個測試數組。 過濾target ,並使用R.anyPass作為謂詞:

 const { curry, unless, is, of, filter, anyPass, map, startsWith } = R; const convertToArray = unless(is(Array), of); const textStartsWith = curry((query, target) => filter(anyPass(map(startsWith, convertToArray(query))))(target) ); const query = ["pen", "paper"]; const target = ["pen", "pencil", "paper", "", "books", "paperback"]; const result = textStartsWith(query, target); console.log(result);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

如果您需要處理非字符串值,我會為每個不是字符串的值返回false 請注意,使用R.toString轉換字符串會轉換字符串 - R.toString('abc'); //=> '"abc"' R.toString('abc'); //=> '"abc"' (參見文檔

 const { curry, unless, is, of, filter, ifElse, anyPass, map, startsWith, always } = R; const convertToArray = unless(is(Array), of); const textStartsWith = curry((query, target) => filter(ifElse( is(String), anyPass(map(startsWith, convertToArray(query))), always(false) ))(target) ); const query = ["pen", "paper"]; const target = ["pen", "pencil", "paper", "", undefined, true, "books", "paperback"]; const result = textStartsWith(query, target); console.log(result);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM