Список группируется динамически - PullRequest
0 голосов
/ 05 мая 2019

Я хочу сгруппировать список, включающий целое число List<int>.

List<CNode> cNodes

и CNode

public class CNode
{
    public List<int> Elements;
    // ...
}

Я могу сгруппировать cNodes вот так

var groups = cNodes.GroupBy(node => node.Elements[0]);
foreach (var group in groups )
{
    // ...
}

но, как вы видите, группировка зависит от первого элемента, я хочу сгруппировать его по всем элементам

Например, если node.Elements.Count == 5 ожидаемый результат группировки должен быть таким же, как для:

var groups = cNodes.GroupBy(node => new
{
    A = node.Elements[0],
    B = node.Elements[1],
    C = node.Elements[2],
    D = node.Elements[3],
    E = node.Elements[4]
});

Я не мог найти решение.

Спасибо.

Ответы [ 2 ]

1 голос
/ 06 мая 2019

Вы писали:

Я хочу сгруппировать его по всем элементам

Решение, данное Алексом, сгруппирует только ограниченное количество элементов. Вы сказали, что хотите сгруппировать его по всем элементам, даже если у вас есть CNode со 100 элементами. Кроме того: его решение также дает сбой, если свойство Elements одного из CNodes равно нулю.

Итак, давайте создадим решение, соответствующее вашим требованиям.

Возвращаемым значением будет последовательность групп, где каждая группа имеет ключ, который представляет собой последовательность CN-кодов. Все элементы в группе являются исходными CN-кодами, которые имеют свойство Elements, равное ключу.

При равном вы имеете в виду SequenceEqual. Итак, Elements [0] == Key [0] и Elements [1] == Key [1] и т. Д.

И, конечно, вы хотите решить, когда Elements [0] равен Key [0]: хотите ли вы сравнить по ссылке (тот же объект)? или два CNodes равны, если они имеют одинаковые значения свойств? Или вы хотите указать IEqualityComparer<CNode>, чтобы вы могли видеть, что они равны, если имеют одинаковое имя или идентификатор?

// overload without IEqualityComparer, calls the overload with IEqualityComparer:
IEnumerable<IGrouping<IEnumerable<Cnode>, CNode>> GroupBy(
    this IEnumerable<CNode> cNodes)
{
    return GroupBy(cNodes, null);
}

// overload with IEqualityComparer; use default CNode comparer if paramer equals null
IEnumerable<IGrouping<IEnumerable<Cnode>, CNode>> GroupBy(
    this IEnumerable<CNode> cNodes,
    IEqualityComparer<CNode> cNodeComparer)
{
    // TODO: check cNodes != null
    if (cNodeComparer == null) cNodeComparer = EqualityComparer<CNode>.Default;

    CNodeSequenceComparer nodeSequenceComparer = new CNodeSequenceComparer()
    {
        CNodeComparer = cNodeComparer,
    }
    return sequenceComparer.GroupBy(nodeSequenceComparer);
}

Вы заметили, что я передал свою проблему новому EqualityComparer: это сравнение берет две последовательности CN-кодов и объявляет их равными, если они SequenceEqual, используя предоставленное IEqualityComparer<CNode>:

class CNodeSequenceComparer : IEqualityComparer<IEnumerable<CNode>>
{
    public IEqualityComparer<CNode> CNodeComparer {get; set;}
    public bool Equals(IEnumerable<CNode> x, IEnumerable<CNode> y)
    {
        // returns true if same sequence, using CNodeComparer
        // TODO: implement
    }
}

Одна из вещей, о которых мы должны помнить, это то, что ваше свойство Elements может иметь значение null (в конце концов, вы не указали, что это не так)

public bool Equals(IEnumerable<CNode> x, IEnumerable<CNode> y)
{
    if (x == null) return y == null; // true if both null
    if (y == null) return false;     // false because x not null

    // optimizations: true if x and y are same object; false if different types
    if (Object.ReferenceEquals(x, y) return true;
    if (x.GetType() != y.GetType()) return false;

    return x.SequenceEquals(y, this.CNodeComparer);
}
1 голос
/ 05 мая 2019

Вы можете использовать что-то вроде node.Take(5) с правильным IEqualityComparer, например:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var cNodes = new List<CNode>
            {
                new CNode{Elements = new List<int>{ 0, 0, 1, 1, 1 } },
                new CNode{Elements = new List<int>{ 0, 0, 0, 1, 1 } },
                new CNode{Elements = new List<int>{ 0, 1, 1, 0 } },
                new CNode{Elements = new List<int>{ 0, 1, 1, 0, 0 } },
                new CNode{Elements = new List<int>{ 0, 0, 0, 0 } },
                new CNode{Elements = new List<int>{ 0, 0, 0, 0 } }
            };

            Console.WriteLine("\tGroup by 2:");
            foreach (var group in cNodes.GroupByElements(2))
                Console.WriteLine($"{string.Join("\n", group)}\n");

            Console.WriteLine("\tGroup by 3:");
            foreach (var group in cNodes.GroupByElements(3))
                Console.WriteLine($"{string.Join("\n", group)}\n");

            Console.WriteLine("\tGroup by all:");
            foreach (var group in cNodes.GroupByElements())
                Console.WriteLine($"{string.Join("\n", group)}\n");
        }
    }

    static class CNodeExtensions
    {
        public static IEnumerable<IGrouping<IEnumerable<int>, CNode>> GroupByElements(this IEnumerable<CNode> nodes) =>
            nodes.GroupByElements(nodes.Min(node => node.Elements.Count));

        public static IEnumerable<IGrouping<IEnumerable<int>, CNode>> GroupByElements(this IEnumerable<CNode> nodes, int count) =>
            nodes.GroupBy(node => node.Elements.Take(count), new SequenceCompare());

        private class SequenceCompare : IEqualityComparer<IEnumerable<int>>
        {
            public bool Equals(IEnumerable<int> x, IEnumerable<int> y) => x.SequenceEqual(y);

            public int GetHashCode(IEnumerable<int> obj)
            {
                unchecked
                {
                    var hash = 17;
                    foreach (var i in obj)
                        hash = hash * 23 + i.GetHashCode();
                    return hash;
                }
            }
        }
    }    

    internal class CNode
    {
        public List<int> Elements;

        public override string ToString() => string.Join(", ", Elements);
    }
}

Вывод:

        Group by 2:
0, 0, 1, 1, 1
0, 0, 0, 1, 1
0, 0, 0, 0
0, 0, 0, 0

0, 1, 1, 0
0, 1, 1, 0, 0

        Group by 3:
0, 0, 1, 1, 1

0, 0, 0, 1, 1
0, 0, 0, 0
0, 0, 0, 0

0, 1, 1, 0
0, 1, 1, 0, 0

        Group by all:
0, 0, 1, 1, 1

0, 0, 0, 1, 1

0, 1, 1, 0
0, 1, 1, 0, 0

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