[英]F# match with query results. Is there an elegant way to do this?
I have an result of a JObject type, from parsing json:通过解析 json,我得到了 JObject 类型的结果:
let j = JObject.Parse x
the code I have to do is like:我必须做的代码是:
if j = null then
... do stuff
else if j["aa"] <> null then
... do stuff
else if j["bb"] <> null then
... do stuff
else if j["cc"] <> null and j["dd"] <> null then
... do stuff
is there a clean way to do this match?有没有一种干净的方法来做这场比赛?
doing statements like做类似的陈述
| _ when j.["error"] <> null ->
doesn't seem super clean.看起来不是很干净。 Can this be done better?
这可以做得更好吗?
let j = JObject.Parse x
let doSomething s = printf "%A" s
if isNull j then
()
else
[ j.["aa"]; j.["bb"]; j.["cc"] ]
|> List.tryFind (fun s -> s |> Option.ofObj |> Option.isSome)
|> doSomething
let j = JObject.Parse x
let doSomething s = printf "%A" s
if isNull j then
()
else
[ j.["aa"]; j.["bb"]; j.["cc"] ]
|> List.choose (fun s -> s |> Option.ofObj)
|> List.iter doSomething
let j = JObject.Parse x
let doSomethingA s = printf "%A" s
let doSomethingB s = printf "%A" s
let doSomethingC s = printf "%A" s
if isNull j then
()
else
[
j.["aa"], doSomethingA
j.["bb"], doSomethingB
j.["cc"], doSomethingC
]
|> List.tryFind (fun (s, _) -> s |> Option.ofObj |> Option.isSome)
|> Option.iter (fun (s, f) -> f s)
If you create an active pattern that returns the matched JToken
...如果您创建一个返回匹配
JToken
的活动模式 ...
let (|NonNull|_|) prop (o : JObject) =
o.[prop] |> Option.ofObj
you could write something like:你可以这样写:
let handleAA (a : JToken) = ()
match JObject.Parse "{}" with
| null -> () // ...
| NonNull "aa" a -> handleAA a
| NonNull "bb" b & NonNull "cc" c -> ()
| _ -> () // all other
Update更新
If you need more power, Active Patterns galore...如果你需要更多的力量,Active Patterns galore...
let (|J|_|) prop (o : obj) =
match o with
| :? JObject as o -> o.[prop] |> Option.ofObj
| _ -> None
let (|Deep|_|) (path : string) (o : obj) =
let get t p = t |> Option.bind (fun t -> (``|J|_|``) p t)
match o with
| :? JToken as t ->
path.Split('.') |> Array.fold get (Option.ofObj t)
| _ -> None
... some helpers... ...一些帮手...
let jV (t : JToken) = t.Value<string>()
let handle t = jV t |> printfn "single: %s"
let handle2 a b = printfn "(%s, %s)" (jV a) (jV b)
... a parse function... ...解析 function...
let parse o =
match JsonConvert.DeserializeObject o with
| null -> printfn "null"
| J "aa" a -> handle a
| J "bb" b & J "cc" c -> handle2 b c
| J "bb" b & J "dd" _ -> handle b
| Deep "foo.bar" bar & Deep "hello.world" world -> handle2 bar world
| Deep "foo.bar" bar -> handle bar
| o -> printfn "val: %A" o
... and off we go: ...然后我们 go:
parse "null" // null
parse "42" // val: 42L
parse "{ aa: 3.141 }" // single: 3.141
parse "{ bb: 2.718, cc: \"e\" }" // (2.718, e)
parse "{ bb: 2.718, dd: 0 }" // single: 2.718
parse "{ foo: { bar: \"baz\" } }" // single: baz
parse "{ foo: { bar: \"baz\" }, hello: { world: \"F#|>I❤\" } }" // (baz, F#|>I❤)
You could create an active pattern to match non-null values...您可以创建一个活动模式来匹配非空值...
let (|NonNull|_|) = function null -> None | v -> Some v
...which would allow the following. ...这将允许以下内容。
if isNull j then
//do stuff
else
match j.["aa"], j.["bb"], j.["cc"], j.["dd"] with
| NonNull aa, _, _, _ -> //do stuff
| _, NonNull bb, _, _ -> //do stuff
| _, _, NonNull cc, NonNull dd -> //do stuff
You could make a list of actions for each key so you could apply the null checking logic uniformly for each one.您可以为每个键制作一个操作列表,以便您可以为每个键统一应用 null 检查逻辑。
let j = JObject.Parse x
let doStuff key value = printfn "%s=>%s" key value
If you wanted to apply doStuff for every key you could iterate though.如果你想为每个键应用 doStuff,你可以迭代。 This is your example but without the else so it does it for every key present.
这是您的示例,但没有 else ,因此它对存在的每个键都执行此操作。
["aa", doStuff
"bb", doStuff
"cc", doStuff]
|> List.iter (fun (key,action) ->
j.TryGetValue key
|> snd
|> Option.ofObj
|> Option.iter (action key))
Matching your example more closely where you only doStuff for the first key present might use choose to get only the valid values,actions.更紧密地匹配您的示例,其中您仅 doStuff 出现的第一个键可能使用选择仅获取有效值、操作。
["aa", doStuff
"bb", doStuff
"cc", doStuff]
|> Seq.choose (fun (key,action) ->
j.TryGetValue key
|> snd
|> Option.ofObj
|> Option.map (fun v -> action key v))
|> Seq.tryHead
This version also returns the result of the applied doStuff if there was a matching key and doStuff returned a value.如果存在匹配的键并且 doStuff 返回了值,则此版本还会返回应用的 doStuff 的结果。 This is abusing the lazy nature of Seq a little bit to only call the first value but you could also map to a function an call the result of Seq.tryHead.
这有点滥用 Seq 的惰性,只调用第一个值,但您也可以 map 到 function 调用 Seq.tryHead 的结果。
Option.ofObj will turn null -> None and v -> Some v Option.ofObj 将变成 null -> None 和 v -> Some v
Since JObject is enumerable you can just map over it由于 JObject 是可枚举的,因此您只需 map 就可以了
let j = JObject.Parse x |> Seq.map Option.ofObj
// j.["aa"] is an Option type now, map will only multiply the value by 3 if it exists
// otherwise it will return none.
let aa = j.["aa"] |> Option.map (fun a -> a * 3)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.