【问题标题】:Simplify multiple (non nested)foreach loops in c#简化 C# 中的多个(非嵌套)foreach 循环
【发布时间】:2020-01-10 18:09:44
【问题描述】:

我有一个 JSON 对象:

"Cars": {
 "Honda": {
   "CRV": [

     { "index": 1, "Color": "Black" },
     { "index": 2, "Color": "White" }

    ],
    "Civic": [

     { "index": 1, "Color": "Blue" }

    ]
   },
  "Toyota": {
   "Corolla": [

     { "index": 1, "Color": "Black" }

    ],
    "Camry": [

     { "index": 1, "Color": "Blue" }

    ]
   },
  "GM": {
   "Chevrolet": {

     "Cruze": [

       { "index": 1, "Color": "Blue" }

    ]

    }
 }

我必须将此信息存储在表格中。现在我正在为每个循环单独解析每个列表,如下所示:

 foreach (CarInfo info in Cars.Honda.CRV)
  {
                //storing in table
  }
  foreach (CarInfo info in Cars.Honda.Civic)
  {
                //storing in table
  }
 foreach (CarInfo info in Cars.Toyota.Corolla)
  {
                //storing in table
  }

等等。 有没有办法优化这段代码? (Given 只是一个例子,实际的 JSON 更广泛,让我写了太多的 foreach 循环)

每个汽车品牌在表格中存储信息的方法不同。

【问题讨论】:

  • 创建一个可以调用三次的方法,一次是Cars.Honda.CRVCars.Honda.Civic,一次是Cars.Toyota.Corolla?将循环放入该方法中...
  • 远离制造商和型号的特定字段。这应该在字典中。
  • 您是否考虑过使用诸如 Json.NET - Newtonsoft.Json 之类的库来为您处理来自 Json 的反序列化?如果它适合您的目的,它可能允许您取消所有的 foreach 循环。
  • @Sepia,我正在使用 NewtonSoft 将 JSON 反序列化为对象。 foreach 循环中的“汽车”对象就是那个反序列化的对象。
  • 啊,好吧。如果每个循环中的“存储在表中”代码都相同,那么 Jon Skeet 的解决方案将是最好的。如果它不同,那么我认为查看一些“存储在表格中”的示例会有所帮助。

标签: c# loops foreach


【解决方案1】:

由于您无法控制输入 JSON,因此这里有一种类似于我上次建议的方法来解析传入的 JSON,包括对 GM 子模型的支持。

static void Main(string[] args)
{
    // parse the JSON results of the API Call
    JObject apiResult = JObject.Parse(File.ReadAllText("JsonBlock.json"));
    // iterate through the models
    foreach (var model in apiResult["Cars"].Children<JProperty>().Select(i => i.Name))
        // GM cars have a submodel
        if(model.Equals("GM",StringComparison.CurrentCultureIgnoreCase)) 
        {
            foreach (var submodel in apiResult["Cars"][model].Children<JProperty>().Select(i => i.Name))
                AddCar(submodel,apiResult["Cars"][model][submodel]);
        } 
        else
            AddCar(model,apiResult["Cars"][model]);            
}

static void AddCar(string model, JToken cars)
{
    switch(model)
    {
        case "Honda":                    
            // do honda things
            break;
        case "Toyota":
            // do toyota things
            break;
        case "Chevrolet":
            // do chevy things
            break;
        default:
            throw new NotImplementedException();
    }            
}

【讨论】:

    【解决方案2】:

    源数据格式是否在您的控制之下?

    • 正如 3Dave 所说,如果汽车品牌和型号是值而不是键,您会更开心。
    • 我可以补充一点,您目前只有一个“汽车”对象。如果您有一个“汽车”对象数组,您可能会发现这更容易管理,每个对象都有其内部的各种属性。
    { "Cars": [
      { "make": "honda",
        "model": "CRV",
        "attributes": ["index":1,"color":"blue"]
      },
      { "make": "honda",
        "model": "CRV",
        "attributes": ["index":2,"color":"white"]
      },
      { "make": "honda",
        "model": "Civic",
        "attributes": ["index":1,"color":"black"]
      }
    ]}
    

    这使得将迭代折叠为单个 foreach 循环,然后在子例程中处理存储在表中的不同方法变得很简单:

    void AddAllCars()
    {
      foreach (var car in Cars)
        AddCar(car);
    }
    
    void AddCar(CarInfo car)
    {
        switch(car.Make)
        {
          case "honda":
            // honda-specific stuff
            break;
          case "toyota":
            // toyota-specific stuff
            break;
        }
    }
    
    (Note that even if you can't reformat into an array of Cars, you can still make big improvements by changing make and model to values instead of keys)
    
    

    【讨论】:

    • 这是一个很好的解决方案,但问题是json来自API调用,真的无能为力。
    【解决方案3】:

    这样的事情怎么样...递归方法,因为你的类不符合 Cars/Make/Model,你有 Cars//Make/Model。

    1. 将 Json 解析为 JObject
    2. 为每个 Make 创建一个字典,以便您可以通过不同的流程分别保存它们
        public static Dictionary<string, List<CarInfo>> GetCarInfo (JObject jObject)
        {
            Dictionary<string, List<CarInfo>> list = new Dictionary<string, List<CarInfo>>();
            foreach (var property in jObject.Properties())
            {
                var thisProp = jObject[property.Name];
                if (thisProp.Type.ToString().Equals("Array"))
                        list.Add(property.Name, JsonConvert.DeserializeObject<List<CarInfo>>(thisProp.ToString()));
                else
                    GetCarInfo((JObject)jObject[property.Name]).ToList().ForEach(x => list.Add(x.Key, x.Value));
    
                }
            return list;
        }
    
        public static void SaveData(string key, List<CarInfo> value)
        {
            switch (key.ToUpper())
            {
                case "CRV":
                    // save it the CRV way
                    break;
    
                case "COROLLA":
                    // save it this way
                    break;
    
                default:
                    throw new ApplicationException("Unable to figure out which car it is..");
            }
        }
    
    

    主要用途

        var cars = JObject.Parse(json);
        Dictionary<string, List<CarInfo>> carInfos = GetCarInfo((JObject)cars["Cars"]);
        foreach (var carInfo in carInfos)
            SaveData(carInfo.Key, carInfo.Value);
    

    注意

    我注意到您的 json 对象并没有真正遵循相同的标准。你的通用汽车有两家母公司,而本田、丰田只有一家。

    建议

    我唯一建议让代码看起来和执行得更好的是更新调用更新/添加到数据库的方式。目前,每种类型都有一个调用,它定义了你的 foreach 循环。如果您可以使用另一个定义“CRV”或“CORROLLA”的参数创建一个通用方法来保存“CarInfo”,这将使您的事情变得更容易。

    【讨论】:

    • 是的。这就是 json 作为 API 调用的响应。
    • 如果您可以制定一种通用的方法来保存数据(使用参数定义您要保存的 Make.Model,您只需调用一次 save 到数据库即可节省大量时间
    猜你喜欢
    • 2022-01-26
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-06
    相关资源
    最近更新 更多