F # as a window sequence based on a predicate, not a fixed length

Given the following input sequence, I would like to generate the desired result. I know that Seq.window can be used to get the desired result if all the windows are of a fixed length. However, in this case they are not fixed legnth, I would like to start a new sequence whenever "a" is encountered. Is this possible in the standard collection library?

let inputSequence = 
      ["a"; "b"; "c";
       "a"; "b"; "c"; "d";
       "a"; "b"; 
       "a"; "d"; "f";
       "a"; "x"; "y"; "z"]

let desiredResult = 
   [["a"; "b"; "c";]
    ["a"; "b"; "c"; "d";]
    ["a"; "b"; ]
    ["a"; "d"; "f";]
    ["a"; "x"; "y"; "z"]]
+4
source share
5 answers

Here is a method that uses a volatile state, but rather concise:

let mutable i = 0
[ for x in inputSequence do
    if x = "a" then i <- i + 1
    yield i, x ]
|> List.groupBy fst
|> List.map snd
|> List.map (List.map snd)
+4
source

, , . , chunkAt, , :

let chunkAt start list = 
  let rec loop chunk chunks list = 
    match list with
    | [] -> List.rev ((List.rev chunk)::chunks)
    | x::xs when x = start && List.isEmpty chunk -> loop [x] chunks xs
    | x::xs when x = start -> loop [x] ((List.rev chunk)::chunks) xs
    | x::xs -> loop (x::chunk) chunks xs
  loop [] [] list

, :

chunkAt "a" inputSequence

, Deedle, . Deedle, , , :

let s = Series.ofValues inputSequence
let chunked = s |> Series.chunkWhile (fun _ k2 -> s.[k2] <> "a")

, Values :

chunked.Values |> Seq.map (fun s -> s.Values)
+3

, , FP- F # . / , . , , , . :

let inputSequence = 
      ["a"; "b"; "c";
       "a"; "b"; "c"; "d";
       "a"; "b"; 
       "a"; "d"; "f";
       "a"; "x"; "y"; "z"]

let aIdx = 
    inputSequence 
        |> List.mapi (fun i x -> i, x) //find the index of a elements
        |> List.filter (fun x -> snd x = "a")
        |> List.map fst //extract it into a list

[List.length inputSequence] 
    |> List.append aIdx //We will need the last "a" index, and the end of the list
    |> List.pairwise //begin and end index
    |> List.map (fun (x,y) -> inputSequence.[x .. (y - 1)]) 

//val it : string list list =
[["a"; "b"; "c"]; ["a"; "b"; "c"; "d"]; ["a"; "b"]; ["a"; "d"; "f"];
["a"; "x"; "y"; "z"]]
+1

, , @TheQuickBrownFox, mutable:

inputSequence 
|> List.scan (fun i x -> if x = "a" then i + 1 else i) 0 
|> List.tail
|> List.zip inputSequence 
|> List.groupBy snd
|> List.map (snd >> List.map fst)

, , @Tomas, F # + :

let splitEvery x = 
    List.split (seq [[x]]) >> Seq.map (List.cons x) >> Seq.tail >> Seq.toList

​​F #, .

0
source

Here is a short one:

let folder (str: string) ((xs, xss): list<string> * list<list<string>>) =
    if str = "a" then ([], ((str :: xs) :: xss))
    else (str :: xs, xss)

List.foldBack folder inputSequence ([], [])
|> snd

// [["a"; "b"; "c"]; ["a"; "b"; "c"; "d"]; ["a"; "b"]; ["a"; "d"; "f"]; ["a"; "x"; "y"; "z"]]

This satisfies the specifications in the question: I would like to start a new sequence whenever "a" is encounteredsince any leading lines before the first "a" will be ignored. For example, for

let inputSequence = 
      ["r"; "s";
       "a"; "b"; "c";
       "a"; "b"; "c"; "d";
       "a"; "b"; 
       "a"; "d"; "f";
       "a"; "x"; "y"; "z"]

it turns out the same result as above.

If you need to wrap the starting lines before the first "a", you can use the following:

match inputSequence |> List.tryFindIndex (fun x -> x = "a") with
| None -> [inputSequence]
| Some i -> (List.take i inputSequence) :: 
            (List.foldBack folder (List.skip i inputSequence) ([], []) |> snd)

// [["r"; "s"]; ["a"; "b"; "c"]; ["a"; "b"; "c"; "d"]; ["a"; "b"];
   ["a"; "d"; "f"]; ["a"; "x"; "y"; "z"]]
0
source

Source: https://habr.com/ru/post/1686291/


All Articles