Я хочу объединить коллекцию строк в одну строку, используя оксфордскую (или последовательную) запятую.
Учитывая
let ss = [ "a"; "b"; "c"; "d" ]
хочу
"a, b, c, and d"
Вот что я придумал.
let oxford (strings: seq<string>) =
let ss = Seq.toArray strings
match ss.Length with
| 0 -> ""
| 1 -> ss.[0]
| 2 -> sprintf "%s and %s" ss.[0] ss.[1]
| _ ->
let allButLast = ss.[0 .. ss.Length - 2]
let commaSeparated = System.String.Join(", ", allButLast)
sprintf "%s, and %s" commaSeparated (Seq.last ss)
Как это можно улучшить?
--- Редактировать ---
Комментарий об многократном повторении последовательностей находится на стадии. Обе реализации ниже избегают преобразования в массив.
Если я использую seq
, мне очень нравится это:
open System.Linq
let oxfordSeq (ss: seq<string>) =
match ss.Count() with
| 0 -> ""
| 1 -> ss.First()
| 2 -> sprintf "%s and %s" (ss.ElementAt(0)) (ss.ElementAt(1))
| _ ->
let allButLast = ss.Take(ss.Count() - 1)
let commaSeparated = System.String.Join(", ", allButLast)
sprintf "%s, and %s" commaSeparated (ss.Last())
Если я использую array
, я также могу избежать итерации Last (), воспользовавшись преимуществами индексации.
let oxfordArray (ss: string[]) =
match ss.Length with
| 0 -> ""
| 1 -> ss.[0]
| 2 -> sprintf "%s and %s" ss.[0] ss.[1]
| _ ->
let allButLast = ss.[0 .. ss.Length - 2]
let commaSeparated = System.String.Join(", ", allButLast)
sprintf "%s, and %s" commaSeparated (ss.[ss.Length - 1]
--- Редактировать ---
Видите эту ссылку от @CaringDev, я думаю, это довольно мило. Никаких подстановочных знаков, обрабатывает нуль, меньше индексации, чтобы получить право, и только обход массива один раз в методе Join ().
let oxford = function
| null | [||] -> ""
| [| a |] -> a
| [| a; b |] -> sprintf "%s and %s" a b
| ss ->
let allButLast = System.ArraySegment(ss, 0, ss.Length - 1)
let sb = System.Text.StringBuilder()
System.String.Join(", ", allButLast) |> sb.Append |> ignore
", and " + ss.[ss.Length - 1] |> sb.Append |> ignore
string sb
Это тоже довольно мило и даже меньше прыгает:
let oxford2 = function
| null | [||] -> ""
| [| a |] -> a
| [| a; b |] -> sprintf "%s and %s" a b
| ss ->
let sb = System.Text.StringBuilder()
let action i (s: string) : unit =
if i < ss.Length - 1
then
sb.Append s |> ignore
sb.Append ", " |> ignore
else
sb.Append "and " |> ignore
sb.Append s |> ignore
Array.iteri action ss
string sb