如何在 F# 中创建一个遍历 JSON 的 for 循环

How do I make a for loop that scans through JSON in F#

我正在尝试使用 F# 扫描 JSON 文件并将其数组与单个(随机生成的)数字数组进行比较。 json 的格式为:

{"1":[#,#,#,#,#],"2":[#,#,#,#,#],...}

等 121 个条目。我目前正在尝试 Json.NET。我的问题是:

这是我的代码:

open System
open System.IO
open Newtonsoft.Json

(*open FSharp.Data

type Provider = JsonProvider<"powernum.json">
let numbers = Provider.Load("powernum.json")

//numbers.``1`` gets me the array but can't scan through all the IDs with an iterating for loop
(*
if numbers.``3`` = [|29;30;41;48;64|] then
    printfn "True"
else printfn "False"
*)
(*numbers.JsonValue.Item "1" 
let test (a: int) = string a
let testPile = 
    for i = 1 to 10 do
    let goNum = numbers.JsonValue.Item (test i) 
    Console.Write goNum
    Console.WriteLine ""
testPile    // This works but is not usable for data comparison with an F# Array
*)
*)

let r = new StreamReader("\PATH\powernum.json")
let (json: string) = r.ReadToEnd();
let conv = JsonConvert.DeserializeObject<> (json)

Console.WriteLine("{0}",conv)//where I'm stuck with Json.NET

[<EntryPoint>]
let main argv =
    let rnd = Random()

    let numberGen = Set.empty.Add(rnd.Next(1,69)).Add(rnd.Next(1,69)).Add(rnd.Next(1,69)).Add(rnd.Next(1,69)).Add(rnd.Next(1,69)) |>Set.toArray |>Array.sort


    Console.ReadKey() |> ignore 
    0// return an integer exit code

Jsontocsharp.com 呈现无效。

我试过使用 F# Data,但从我发现的情况来看,不可能进行迭代循环,因为我必须拉起 "key" 并用重音封装数字以将其读取为 int 之类的这个numbers.``1``。它不接受变量。在仍然使用 F# Data 的同时尝试了另一种方法,但它只作为一个字符串工作,当我尝试转换它时出错。

为了比较,这是我在 python 中制作原型的版本:

import random
import json
with open('/PATH/powernum.json') as pnumbers:
    data = json.load(pnumbers)

#makes an array with the range of numbers
Valid_numbers =[]

for x in range(69):
    Valid_numbers.append(x+1)
generated = []

def pulledNumber():
    generated[:]=[]
    #adds numbers to the array from 0-4
    while len(generated) !=5:
        #takes a random number from the range of numbers
        generate_number = random.choice(Valid_numbers)
        #check if the two arrays have the same values
        if generate_number not in generated:
            #add to the array if values don't match
            generated.append(generate_number)
    generated.sort()
    return generated

pulledNumber()

for x, y in data.items():
    if generated not in y:
        print("Id: %s passed" % x)
    else:
        print("Id: %s failed" % x)
        pulledNumber()
        break

print (pulledNumber())

f# 是一种静态类型语言——我们只是经常没有注意到它,因为它具有出色的类型推断功能。但是,当从 JSON 文件反序列化时,在编写任何代码之前,确定 JSON 是否具有固定模式是很有用的,如果是,则创建或选择适当的数据模型,JSON可以自动映射。

在你的情况下,你的 JSON 看起来像:

{
  "1": [29,30,41,48,64],
  "2": [29,320,441,548,11]
  // Additional items omitted
}

当你在这里有一个根对象时,它的变量名是属性,它的值总是一个整数数组。如 Newtonsoft 文档 Deserialize a Dictionary, such a JSON object can be deserialized to some IDictionary<string, T> for appropriate value type T. And as explained in Deserialize a Collection 中所述,JSON 数组可以反序列化为适当项目类型的集合。

在 f# 中,我们使用 Map<'Key,'Value> to represent a dictionary, and lists 来表示静态类型值的物化列表。因此,您的 JSON 对应于

Map<string, int list>

确定合适的数据模型后,引入以下函数从文件中反序列化 JSON:

//https://www.newtonsoft.com/json/help/html/DeserializeWithJsonSerializerFromFile.htm
let jsonFromFile<'T>(fileName : string, settings : JsonSerializerSettings) = 
    use streamReader = new StreamReader(fileName)
    use jsonReader = new JsonTextReader(streamReader)
    let serializer = JsonSerializer.CreateDefault(settings)
    serializer.Deserialize<'T>(jsonReader)

现在您可以反序列化 JSON 并过滤匹配某些

的列表的值
let fileName = "\PATH\powernum.json"

let requiredValues = [29;30;41;48;64] // Or whatever list of values you are looking for

let map = jsonFromFile<Map<string, int list>>(fileName, null)

let filteredMap = 
    map |> Map.toSeq
        |> Seq.filter (fun (key, value) -> requiredValues = value)
        |> Map.ofSeq

// Print results
filteredMap |> Map.iter (fun key value ->
   printf "Key = %A has matching list of values = %A\n" key value)

打印出来

Key = "1" has matching list of values = [29; 30; 41; 48; 64]

备注:

  • 始终确保处理 disposable resources such as StreamReader after you are done with them. The use 关键字以确保在资源超出范围时自动发生这种情况。

  • 如果您希望搜索 无序集合 值,您可以使用 set 而不是 list

    let requiredValues = set [64;29;30;41;48] // Or whatever set of values you are looking for
    
    let map = jsonFromFile<Map<string, Set<int>>>(fileName, null)
    
    let filteredMap = 
        map |> Map.toSeq
            |> Seq.filter (fun (key, value) -> requiredValues = value)
            |> Map.ofSeq
    
  • 正如 Don Syme 在 Equality and Comparison Constraints in F# 中所解释的,listset 都支持结构相等比较,这这就是为什么 requiredValues = value 检查集合是否具有相同的 contents.

演示 fiddle #1 here for list and #2 here set.