【问题标题】:multiple Groupby on result of GroupBy LinqGroupBy Linq 结果上的多个 Groupby
【发布时间】:2017-04-22 09:57:11
【问题描述】:

假设我有两个班级

class Foo
{
 List<Bar> Bars{get;set;} 
}

class Bar
{
  string Name{get;set;}
  string Value{get;set;}
}

我有一个这样的查询

var resultsGroupedByState=Foos.GroupBy(x=>x.Bars.First(c=>c.Name=="state").Value);

这将返回按状态分组的结果,此外,用户可以定义按字段分组的层次结构,例如

  1. 按州分组
  2. 按县分组
  3. groupBy City 等等

我要做多少级 groupBy,我不知道。我必须先在层次结构中定义一个 groupby,然后根据层次结构中的第二个值对第一个 groupby 的结果进行 groupby,然后继续。

最后,我必须像这样从该数据生成一个 xml。

<State value="anyState">
   <County value="anyCounty">
       <City value="anyCity">
          ..........
        </City>
    </County>
 </State>

【问题讨论】:

    标签: c# linq grouping


    【解决方案1】:

    基本上你需要生成 XML。一种解决方案是递归地执行GroupBy 并直接创建XMLDocument。

    这是一个可以做到这一点的辅助类。

    public class XmlGenerator
    {
        private XmlDocument _document;
        private List<string> _fields;
        private int index = 0;
    
        public XmlGenerator(List<string> fields)
        {
            _fields = fields;
        }
    
        public void GenerateXML(IEnumerable<Foo> lookupData, XmlElement root, string field)
        {
            var fieldGrouping = lookupData.GroupBy(v => v.Bars.First(x => x.Name == field).Value);
            foreach (var grouping in fieldGrouping)
            {
                var element = _document.CreateElement(field);
                var attrib = _document.CreateAttribute("value");
                attrib.InnerText = grouping.Key;
    
                element.Attributes.Append(attrib);
                root.AppendChild(element);
    
                // If that is last field. No need to do any grouping.
                if (index < _fields.Count - 1)
                {
                    // After each nested call update the nested level.
                    // If GenerateXML do another that will be upon next nested level
                    index += 1;
                    GenerateXML(grouping, element, _fields[index]);
                    index -= 1;
                    // Make sure to change back nested level index otherwise that will not works if you have multiple groups.
                }
            }
        }
    
        public string GenerateXML(IEnumerable<Foo> lookupData)
        {
            _document = new XmlDocument();
            var root = _document.CreateElement("Root");
            _document.AppendChild(root);
            index = 0;
    
            GenerateXML(lookupData, root, _fields[index]);
    
            return _document.OuterXml;
        }
    }
    

    实际上我在这里所做的是,我将所有字段列表存储在List&lt;string&gt;. 中,并编写了一个按特定字段分组的辅助方法。每次传递时,我都会传递需要分组的方法数据和要分组的字段名称。

    希望如此,这可以解决您的问题。

    【讨论】:

    • 感谢您指出我一直在寻找的解决方案,但我被困在首先制作该层次结构然后遍历该层次结构以生成 xml,无论如何它解决了我的问题。
    【解决方案2】:

    您不需要 GroupBy。试试这个

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Xml.Linq;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
               string xml =
                   "<Root>" +
                        "<State value=\"AState\">" +
                           "<County value=\"ACounty\">" +
                               "<City value=\"ACity\"/>" +
                               "<City value=\"BCity\"/>" +
                               "<City value=\"CCity\"/>" +
                            "</County>" +
                           "<County value=\"BCounty\">" +
                               "<City value=\"ACity\"/>" +
                               "<City value=\"BCity\"/>" +
                               "<City value=\"CCity\"/>" +
                            "</County>" +
                           "<County value=\"CCounty\">" +
                               "<City value=\"ACity\"/>" +
                               "<City value=\"BCity\"/>" +
                               "<City value=\"CCity\"/>" +
                            "</County>" +
                         "</State>" +
                        "<State value=\"BState\">" +
                           "<County value=\"ACounty\">" +
                               "<City value=\"ACity\"/>" +
                               "<City value=\"BCity\"/>" +
                               "<City value=\"CCity\"/>" +
                            "</County>" +
                           "<County value=\"BCounty\">" +
                               "<City value=\"ACity\"/>" +
                               "<City value=\"BCity\"/>" +
                               "<City value=\"CCity\"/>" +
                            "</County>" +
                           "<County value=\"CCounty\">" +
                               "<City value=\"ACity\"/>" +
                               "<City value=\"BCity\"/>" +
                               "<City value=\"CCity\"/>" +
                            "</County>" +
                         "</State>" +
                         "<State value=\"CState\">" +
                           "<County value=\"ACounty\">" +
                               "<City value=\"ACity\"/>" +
                               "<City value=\"BCity\"/>" +
                               "<City value=\"CCity\"/>" +
                            "</County>" +
                           "<County value=\"BCounty\">" +
                               "<City value=\"ACity\"/>" +
                               "<City value=\"BCity\"/>" +
                               "<City value=\"CCity\"/>" +
                            "</County>" +
                           "<County value=\"CCounty\">" +
                               "<City value=\"ACity\"/>" +
                               "<City value=\"BCity\"/>" +
                               "<City value=\"CCity\"/>" +
                            "</County>" +
                         "</State>" +
                        "</Root>";
    
               XElement root = XElement.Parse(xml);
    
               List<Foo> foos = Helper(root); 
    
            }
            public static List<Foo> Helper(XElement element)
            {
                if (element.HasElements)
                {
                    string tagName = ((XElement)element.FirstNode).Name.LocalName;
    
                    List<Foo> children = element.Elements(tagName).Select(x => new Foo()
                    {
                        Name = tagName,
                        Value = (string)x.Attribute("value"),
                        foos = Helper(x)
                    }).ToList();
    
    
                    return children;
                }
                else
                {
                    return null;
                }
            }
        }
        public class Foo
        {
            public string Name { get; set; }
            public string Value { get; set; }
    
            public List<Foo> foos { get; set; }
        }
    
    }
    

    【讨论】:

    • 虽然此代码片段可能会回答 OP 的问题,但如果您添加解释,答案会更有用。请注意,仅代码的答案往往会被标记为质量审查,这可能会导致它们被删除。
    • 这段代码从 xml 到 Foos 列表。我认为问题恰恰相反:从 Foos 列表到 xml。
    • 是的@vyrp 是对的,您的解决方案与我想要的相反。无论如何感谢您的努力
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-17
    • 1970-01-01
    • 2020-01-22
    • 1970-01-01
    • 2011-04-25
    相关资源
    最近更新 更多