Как сгруппировать присоединиться к плоской XDocument? - PullRequest
1 голос
/ 29 декабря 2011

У меня есть XDocument с фрагментом, похожим на:

<Data>
    <Row Id="0" ParentId="-1">
        <!-- stuff -->
    </Row>
    <Row Id="1" ParentId="0">
        <!-- stuff -->
    </Row>
    <Row Id="2" ParentId="0">
        <!-- stuff -->
    </Row>
    <Row Id="3" ParentId="-1">
        <!-- stuff -->
    </Row>
    <Row Id="4" ParentId="3">
        <!-- stuff -->
    </Row>
</Data>

Предположим, что вложение ограничено приведенным выше примером.Я хочу создать структуру данных - IDictionary<Parent, List<Child>>.Кажется, я не могу получить ничего, чтобы присоединиться правильно.Что я имею к этому пункту:

// get lists of data nodes
List<XElement> pRows = xData.Elements(XName.Get("Row"))
                            .Where(e => e.Attribute(XParentId).Value == "-1")
                            .Select(e => e)
                            .ToList();
List<XElement> cRows = xData.Elements(XName.Get("Row"))
                            .Where(e => e.Attribute(XParentId).Value != "-1")
                            .Select(e => e)
                            .ToList();

var dataSets = pRows.GroupJoin(cRows,
                               p => p,
                               c => c.Attribute(XParentId).Value,
                               (p, children) => new {
                                 ParentID = p.Attribute(XId).Value,
                                 Children = children.Select(c => c)
                               });

Компилятор жалуется:

Аргументы типа для метода 'System.Linq.Enumerable.GroupJoin (System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable, System.Func, System.Func, System.Func, TResult>) 'не может быть выведен из использования.Попробуйте явно указать аргументы типа.

Я следовал примеру из MSDN, используя GroupJoin .Я не хотел использовать 2 списка - я бы предпочел использовать один список List<XElement>, содержащий все строки.

Ответы [ 2 ]

1 голос
/ 31 декабря 2011

Я думаю, что подход с двумя списками более чистый, за исключением того, что я бы избегал вызова ToList() до последнего шага или когда список действительно необходим. Вы можете превратить это в одно утверждение, но за ним будет долго и труднее следовать.

Чтобы исправить ваш запрос, вам нужно изменить селектор внешнего ключа для pRows с p => p на p => p.Attribute(XId).Value, который является действительным идентификатором. В настоящее время он выбирает весь элемент, который нельзя сравнить с c => c.Attribute(XParentId).Value, поскольку они имеют разные типы. Обновленный запрос будет:

var dataSets = pRows.GroupJoin(cRows,
                               p => p.Attribute(XId).Value,
                               c => c.Attribute(XParentId).Value,
                               (p, children) => new {
                                 ParentID = p.Attribute(XId).Value,
                                 Children = children.Select(c => c)
                               });

Чтобы не использовать 2 списка, вы можете изменить приведенный выше запрос и заменить pRows и cRows их соответствующими запросами, но это делает его длинным и тяжелым для глаз. В этом конкретном случае я предпочитаю выражать GroupJoin с использованием синтаксиса запроса, поскольку его гораздо легче читать, чем свободный синтаксис:

var query = from root in xData.Elements("Row").Where(e => e.Attribute("ParentId").Value == "-1")
            join child in xData.Elements("Row").Where(e => e.Attribute("ParentId").Value != "-1")
            on root.Attribute("Id").Value equals child.Attribute("ParentId").Value
            into rootChild
            select new 
            {
                ParentId = root.Attribute("Id").Value,
                Children = rootChild.Select(o => o)
            };

var dict = query.ToDictionary(o => o.ParentId, o => o.Children.ToList());

Если у вашей реальной проблемы больше вложенности, LINQ, вероятно, не будет идеальным решением.

0 голосов
/ 31 декабря 2011

Во-первых, для компиляции кода вы можете заменить параметр p => p в GroupJoin на p => p.Attribute(XId).Value, который выбирает ключ для сравнения.

В результате вы получите IEnumerableс объектами

  • ParentID = 0, IEnumerable<XElement> {Идентификатор строки = 1, Идентификатор строки = 2}
  • Идентификатор строки = 3, IEnumerable<XElement> {Идентификатор строки = 4}

Конечно, вы также можете изменить .Select(c => c), чтобы он возвращал List<string> только с идентификаторами (.Select(c => c.Attribute(XId).Value).ToList()), но вам все еще не хватает ParentID = -1 иу вас нет словаря.

Если вы хотите включить ParentID = -1, а также получить Dictionary<string,List<string>>, то вы можете попробовать это (в качестве точки для начала), которая используетдругой подход:

// get all ParentIds
var allParentIds = xData.Elements(XName.Get("Row"))
                        .Select(e => e.Attribute(XParentId).Value)
                        .Distinct();
// then use them to get all Ids where the ParentId is in the "list" of all ParentIds
var parentIdsAndChilds = from givenIds
                         in allParentIds
                         select new {
                             Id = givenIds,
                             Childs = xData.Elements(XName.Get("Row")).Where(e => e.Attribute(XParentId).Value == givenIds).Select(e => e.Attribute(XId).Value).ToList()
                         };
// if needed, convert it to a Dictionary
Dictionary<string, List<string>> dict = parentIdsAndChilds.ToDictionary(k => k.Id, v => v.Childs);

Надеюсь, это поможет или, может быть, послужит отправной точкой для дальнейших исследований.

Примечание с одной стороны: как вы, возможно, знаете, LINQ использует отложенное выполнение , поэтому вы не можете вызывать ToList() на каждом шаге, чтобы сэкономить время выполнения (в зависимости от общего объема данных, это может быть хорошей идеейo время от времени использовать ToList(), потому что это может сэкономить память во время обработки, но это зависит от реальных данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...