【问题标题】:c# Subtract one json's property int value from another based on another property's same valuec# 根据另一个属性的相同值,从另一个中减去一个json的属性int值
【发布时间】:2020-10-13 06:52:47
【问题描述】:

我有两个 jsons 数据: json1:

[
{"name":"a1", "quantity": 10 },
{"name":"a2", "quantity": 11 },
{"name":"a3", "quantity": 12 },
{"name":"a4", "quantity": 13 },
{"name":"a5", "quantity": 14 },
]

Json2:

[
{"name":"a1", "quantity": 11 },
{"name":"b1", "quantity": 1 },
{"name":"b2", "quantity": 12 },
{"name":"a3", "quantity": 13 },
{"name":"a5", "quantity": 14 },
]

我希望 Json1 的数量减去 Json2 基于相同“名称”的数量

结果将返回所有 Json1 的项目加上另一个属性“不同”。

[
{"name":"a1", "quantity": 10, "differ": -1 },
{"name":"a2", "quantity": 11, "differ": 11  }, // "a2" is not in Json2, so "differ" will be 11
{"name":"a3", "quantity": 12, "differ": -1 },
{"name":"a4", "quantity": 13, "differ": 13 },
{"name":"a5", "quantity": 14, "differ": 0 },
]

当使用下面的解决方案时,输入数据(Json1 和 Json2)有超过 3000-5000 个项目大约需要 2 秒。寻找性能更好的新解决方案。用大约 5000 个项目说不到 1 秒。
C#代码:

public string GetDiff(string json1, string json2)
{
            var json1Array = JArray.Parse(json1);
            var json2Array = JArray.Parse(json2);
            var json3Array = new JArray();
            foreach (var item in json1Array)
            {
                var name = (string) item["name"];
                var quantity = (int) item["quantity"];
                var differ = quantity;
                var itemJson2 = json2Array.Where(it => (string) it["name"] == name).FirstOrDefault();
                if (itemJson2 != null)
                {
                    differ = quantity - (int) itemJson2["quantity"];
                }
                json3Array.Add(new JObject() { { "name", name }, { "quantity", quantity }, { "differ", differ } });
            }
            result = JsonConvert.SerializeObject(json3Array);

}

【问题讨论】:

  • 或者您可以使用 Text.Json 或 json.net 对其进行反序列化,让您的生活更轻松。无论如何你必须完全解析它
  • 与其写伪代码,我建议你在真实代码中实现你当前的想法,并衡量性能。你说它会有“不好的性能”——这是否意味着你有具体的性能要求?您当前的想法与这些要求相比如何?
  • @JonSkeet,感谢您的评论。输入数据Json1和Json2各有3000-5000多条,用我的方法大概需要2秒,不够好,一定有更好的。我希望不到 1 秒。
  • 对,所以大概你现在有实际代码而不是伪代码 - 请用真实代码更新你的问题,并包括你的性能要求方面。

标签: c# arrays json performance subtraction


【解决方案1】:

我已经使用您的方法和使用字典的不同方法对其进行了测试。您可以在这里找到它作为 dotnetfiddle:https://dotnetfiddle.net/rS0Am8

using System;
using System.Diagnostics;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class Program
{
    public void Main()
    {
        var json1 = @"[
        {""name"":""a1"", ""quantity"": 10 },
        {""name"":""a2"", ""quantity"": 11 },
        {""name"":""a3"", ""quantity"": 12 },
        {""name"":""a4"", ""quantity"": 13 },
        {""name"":""a5"", ""quantity"": 14 },
        ]";
        var json2 = @"[
        {""name"":""a1"", ""quantity"": 11 },
        {""name"":""b1"", ""quantity"": 1 },
        {""name"":""b2"", ""quantity"": 12 },
        {""name"":""a3"", ""quantity"": 13 },
        {""name"":""a5"", ""quantity"": 14 },
        ]";
        var retries = 10000;

        var sw = new Stopwatch();

        sw.Start();

        string result = "";

        for (var i = 0; i < retries; i++)
        {
            var json1Array = JArray.Parse(json1);
            var json2Array = JArray.Parse(json2);
            var json3Array = new JArray();
            foreach (var item in json1Array)
            {
                var name = (string) item["name"];
                var quantity = (int) item["quantity"];
                var differ = quantity;
                var itemJson2 = json2Array.Where(it => (string) it["name"] == name).FirstOrDefault();
                if (itemJson2 != null)
                {
                    differ = quantity - (int) itemJson2["quantity"];
                }
                json3Array.Add(new JObject() { { "name", name }, { "quantity", quantity }, { "differ", differ } });
            }
            result = JsonConvert.SerializeObject(json3Array);
        }

        sw.Stop();
        Console.WriteLine("Variant 1 (" + sw.ElapsedMilliseconds + "ms)");
        Console.WriteLine(result);

        sw.Restart();
        for (var i = 0; i < retries; i++)
        {
            var d1 = JsonConvert.DeserializeObject<JsonData[]>(json1).ToDictionary(d => d.Name, d => d.Quantity);
            var d2 = JsonConvert.DeserializeObject<JsonData[]>(json2).ToDictionary(d => d.Name, d => d.Quantity);

            result = JsonConvert.SerializeObject(d1.Select(kvp => new JsonResultData
            {
                Name = kvp.Key,
                Quantity = kvp.Value,
                Differ = d2.ContainsKey(kvp.Key) ? kvp.Value - d2[kvp.Key] : kvp.Value
            }));
        }

        sw.Stop();
        Console.WriteLine("Variant 2 (" + sw.ElapsedMilliseconds + "ms)");
        Console.WriteLine(result);
    }

    // Define other methods and classes here
    class JsonData
    {
        [JsonProperty("name", Order = 1)]
        public string Name { get; set; }

        [JsonProperty("quantity", Order = 2)]
        public int Quantity { get; set; }
    }

    class JsonResultData : JsonData
    {
        [JsonProperty("differ", Order = 3)]
        public int Differ { get; set; }
    }
}

在 DotNetFiddle 中,您的方法似乎更快(大约是 5 到 10 倍)。但是,在 LINQPad 中本地运行时,变体 2 的运行速度提高了两倍。

我认为,这在很大程度上取决于您的输入,以及您将进行多少次迭代。但是,正如 Jon Skeet 建议的那样:实际尝试总是最好的。这就是开发 LINQPad 等工具的目的:-)

更新:

另外,不要使用JsonConvert.SerializeObject(...) 生成结果 JSON,而是手动创建它也会提高您的性能。但是,我不建议这样做,除非您的 Json 结构仍然像您描述的那样简单。在我的测试中,这节省了大约 30%。示例实现:

sw.Restart();
for (var i = 0; i < retries; i++)
{
    var d1 = JsonConvert.DeserializeObject<JsonData[]>(json1).ToDictionary(d => d.Name, d => d.Quantity);
    var d2 = JsonConvert.DeserializeObject<JsonData[]>(json2).ToDictionary(d => d.Name, d => d.Quantity);

    var sb = new StringBuilder();
    sb.Append("[");
    foreach (var kvp in d1)
    {
        sb.AppendFormat("{{\"name\":\"{0}\",\"quantity\":{1},\"differ\":{2}}}",
            kvp.Key.Replace("\"", "\\\""),
            kvp.Value.ToString(CultureInfo.InvariantCulture),
            (d2.ContainsKey(kvp.Key) ? kvp.Value - d2[kvp.Key] : kvp.Value).ToString(CultureInfo.InvariantCulture));
    }
    sb.Append("]");
    result = sb.ToString();
}

sw.Stop();
Console.WriteLine("Variant 3 (" + sw.ElapsedMilliseconds + "ms)");
Console.WriteLine(result);

【讨论】:

  • 感谢@Christoph Herold,正如您所说,这取决于输入数据。我将使用我的数据尝试你的方法,每个 Json 中有 3000 多个项目。
  • @uou 我刚刚尝试使用更大的 json(5000 个条目,正如您在上面的评论中提到的那样):字典方法会快得多,因为您的迭代搜索将在字典查找中失败。我得到的时间是 1,666 毫秒与 47 毫秒,因此一次迭代的速度提高了 35 倍(!)。当我使用 10 的循环时,我什至得到了 90 的加速因子。如果这对你有用,请告诉我:-)
  • 谢谢,有时间来测试你的解决方案。效果超级好 5700 个条目只需要 30-40 毫秒。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-29
  • 1970-01-01
  • 1970-01-01
  • 2016-06-03
  • 1970-01-01
相关资源
最近更新 更多