【问题标题】:Reduce nested list of object by parameter按参数减少对象的嵌套列表
【发布时间】:2020-05-31 05:10:02
【问题描述】:

我需要将一个嵌套列表集合缩减为另一个。我只需要保留具有参数 IsOk == true 的对象。

public class Item {
public string Id { get; set; }  
public Item[] Items { get; set; }
public bool IsOk { get; set; }

这是输入json:

                {
                'id': '1',
                'isOk': false,
                'items': [
                    {
                        'id': '21',
                        'isOk': false,
                        'items': [
                            {
                                'id': '31',
                                'isOk': true,
                                'items': [
                                    {
                                        'id': '41',
                                        'isOk': true,
                                        'items': null
                                    },
                                    {
                                        'id': '42',
                                        'isOk': true,
                                        'items': null
                                    }
                                ]
                            }
                        ]
                    },
                    {
                        'id': '22',
                        'isOk': true,
                        'items': [
                            {
                                'id': '32',
                                'isOk': true,
                                'items': [
                                    {
                                        'id': '43',
                                        'isOk': true,
                                        'items': null
                                    },
                                    {
                                        'id': '44',
                                        'isOk': true,
                                        'items': null
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }

这是正确的修改后的 json。

        [
            {
                'id': '31',
                'isOk': true,
                'items': [
                    {
                        'id': '41',
                        'isOk': true,
                        'items': null
                    },
                    {
                        'id': '42',
                        'isOk': true,
                        'items': null
                    }
                ]   
            },
                {
                    'id': '22',
                    'isOk': true,
                    'items': [
                        {
                            'id': '32',
                            'isOk': true,
                            'items': [
                                {
                                    'id': '43',
                                    'isOk': true,
                                    'items': null
                                },
                                {
                                    'id': '44',
                                    'isOk': true,
                                    'items': null
                                }
                            ]
                        }
                    ]
                }
            ]

如你所见,新的 json 修改了层次结构,只保留属性 IsOk == true 的对象。 对我来说最大的问题是如何改变对象的层次结构。 目前我尝试使用递归函数,但也许更好的是使用while循环?

【问题讨论】:

  • 您是否使用标准 JSON 解析库将 JSON 解析为 C# 对象?例如。 stackoverflow.com/questions/6620165/how-can-i-parse-json-with-c
  • 这只是结果应该是什么样子的示例。我只有第一个 json,我需要构建对象以将其序列化为第二个 json。
  • 听起来你想先展平你的结构,然后只得到那些'IsOk'。如果是这样,这个答案建议使用LINQ-to-JSON我的帮助。
  • 但是我怎样才能保留最后一个可用的父级
  • 这应该是树的数据结构吗?是否允许两个以上的项目作为一个项目的后代?您当前所要求的根本不是微不足道的,因为层次结构的更改也会导致数据结构的更改(想象 ID=1 的 Item 作为树的根节点,如果您在不替换的情况下将其删除你没有一棵树了,但有两棵)。我的意思是,这可以解决,但不仅仅是通过反序列化 json 并且不使用合适的数据结构(可能是二叉搜索树?)。

标签: c# algorithm


【解决方案1】:

你可以试试这个。正如我在对该问题的评论中所说,这不是一项微不足道的任务。以下 5 种方法使用 cmets 进行了解释,底部有一个如何使用它们的示例。我在 json 的不同配置上对其进行了测试,似乎可以工作。

        /// <summary>
        /// Flattens the actual result of the json deserialization (a List<Item>).
        /// </summary>
        /// <param name="deserializedObject">The actual result of the json deserialization (a List<Item>).</param>
        /// <param name="flattenedObject">The resulting flattened object.</param>
        private void FlattenDeserializedObject(List<Item> deserializedObject, List<Item> flattenedObject)
        {
            // Null-check on the flattened list.
            if (flattenedObject is null)
                flattenedObject = new List<Item>();
            // Iterate on every Item of the structure.
            for (var i = 0; i < deserializedObject.Count; i++)
            {
                if (deserializedObject[i] is null) continue;
                // Add the item to the flattened structure.
                flattenedObject.Add(deserializedObject[i]);
                // Check if there are descendants, if no the step to the next Item.
                if (deserializedObject[i].Items is null || deserializedObject[i].Items.Count() == 0)
                    continue;
                // There are descendants, recurse on them and update flattenedStructure.
                FlattenDeserializedObject(deserializedObject[i].Items.ToList(), flattenedObject);
            }
        }
        /// <summary>
        /// Creates a dictionary of descendants of an Item having IsOk=true.
        /// Dictionary KEY=[ItemID, VALUE=List of descendants with IsOk=true].
        /// </summary>
        /// <param name="flattenedObject">The flattened list of Items.</param>
        /// <param name="descIds">Dictionary KEY=[ItemID, VALUE=List of descendants with IsOk=true]</param>
        private void GetValidDescendantsIdDictionary(List<Item> flattenedObject, out Dictionary<string, List<string>> descendantsDictionary)
        {
            // Create an instance of the dictionary.
            descendantsDictionary = new Dictionary<string, List<string>>();
            // Get the descendants for every Item.
            foreach (var node in flattenedObject)
            {
                // Create a new list of ids for this Item id.
                descendantsDictionary.Add(node.Id, new List<string>());
                // Get all the isOk=true descendants for a specific node.
                GetValidNodeDescendantsIds(node, descendantsDictionary[node.Id]);
            }
        }
        /// <summary>
        /// Gets all the isOk=true descendants of a specific Item node.
        /// </summary>
        /// <param name="node">Item node.</param>
        /// <param name="descendantIds">List of isOK=true descendant IDs for the node.</param>
        private void GetValidNodeDescendantsIds(Item node, List<string> descendantIds)
        {
            // Null-empty check on the nodes and the direct descendants.
            if (node is null || node.Items is null || node.Items.Length == 0)
                return;
            descendantIds.AddRange(node.Items.Where(x => x.IsOk).Select(x => x.Id));
            // Recurse on the children of the node.
            node.Items.ToList().ForEach(n => GetValidNodeDescendantsIds(n, descendantIds));
        }
        /// <summary>
        /// Removes all the isOk=false items from the flattened structure.
        /// </summary>
        private void RemoveInvalidItemsFromFlattenedObject(List<Item> flattenedObject)
        {
            // Null-empty check on the flattened list.
            if (flattenedObject is null || flattenedObject.Count == 0) return;
            flattenedObject.RemoveAll(x => !x.IsOk);
        }
        /// <summary>
        /// Rebuilds the original structure node by node from the flattened one (without the invalid elements).
        /// Uses the descendants ID dictionary to get the correct position for the new nodes.
        /// Result will be in flattenedStructure.
        /// </summary>
        private void RebuildOriginalStructureFromFlattened(Item node, List<Item> flattenedStructure, Dictionary<string, List<string>> descendantsDictionary)
        {
            if (node is null || node.Items is null || node.Items.Length == 0)
                return;
            // Get the list of descendants from the support dictionary.
            var nodeDescendants = descendantsDictionary[node.Id];

            if (nodeDescendants.Count > 0)
            {
                // If there are isOk descendants, check their level and fix the hierarchy.
                var nodeLevel = int.Parse(node.Id.Substring(0, 1));
                var nextLevelWithNodes = nodeDescendants.Select(x => int.Parse(x.Substring(0, 1))).Min();
                var nextLevelNodeIds = nodeDescendants.Where(x => int.Parse(x.Substring(0, 1)) == nextLevelWithNodes);
                node.Items = flattenedStructure.Where(x => nextLevelNodeIds.Contains(x.Id)).ToArray();
                flattenedStructure.RemoveAll(x => nextLevelNodeIds.Contains(x.Id));
                // Recurse on every children.
                foreach (var n in node.Items)
                    RebuildOriginalStructureFromFlattened(n, flattenedStructure, descendantsDictionary);
            }
            else
            {
                // There are no isOk descendants, reset the Items array to null.
                node.Items = null;
            }
        }

用法:

            // json is the path to the json file.
            var items = JsonConvert.DeserializeObject<List<Item>>(json);
            // Create a list for the flattened structure.
            var flattenedObject = new List<Item>();
            // 1) Fill the flattenedObject list by passing the original structure (items).
            FlattenDeserializedObject(items, flattenedObject);
            // 2) Creates a dictionary of valid (isOk=true) descendants for each node.
            GetValidDescendantsIdDictionary(flattenedObject, out Dictionary<string, List<string>> descIds);
            // 3) Remove all the isOk=false nodes from the flattened structure.
            RemoveInvalidItemsFromFlattenedObject(flattenedObject);
            // 4) Rebuild the original structure from the flattened one.
            for (var i=0; i< flattenedObject.Count; i++)
                RebuildOriginalStructureFromFlattened(flattenedObject[i], flattenedObject, descIds);
            // 5) Get the json without the isOk=false nodes, but keeping the initial structure.
            var okJson = JsonConvert.SerializeObject(flattenedObject);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-07
    • 2020-09-18
    • 2018-01-12
    • 2018-05-30
    • 1970-01-01
    • 2019-09-15
    • 1970-01-01
    • 2019-10-16
    相关资源
    最近更新 更多