LINQ Запрос по Dictionary с битовой маской

213
03 мая 2018, 06:41

Помогите, пожалуйста, решить задачку. Дано:

public enum GroupType   {   None    = 0x0000
                        ,   Name    = 0x0001    
                        ,   Ext     = 0x0002
                        ,   Type    = 0x0004
                        };
Dictionary<int, GroupType> d;
d = new Dictionary<int, GroupType> { {1, GroupType.Name}
                                   , {2, GroupType.Name | GroupType.Type}
                                   , {3, GroupType.Type}
                                   };

В результате должно получиться

Dictionary<GroupType, List<int>> dRes;
dRes = { {GroupType.Name, {1, 2}}
       , {GroupType.Type, {2, 3}}
       };

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

Answer 1

GroupType.None битовая маска «0» введена для того, чтобы выбирать именно элементы без всех ключей. Сравнение идет по равенству. Чтобы не сравнивать == 0, а делать == GroupType.None

Enum.HasFlag для нулевой маски возвращает всегда true, т.ч. нужно будет написать вспомогательный метод, чтобы реализовать подобное сравнение:

private static bool HasFlag(GroupType value, GroupType flag)
{
    if(flag==GroupType.None) return ((int) value)==0;
    return value.HasFlag(flag);
}

После этого можно выбрать все значения перечисления в список:

var types = Enum.GetValues(typeof(GroupType))
                .Cast<GroupType>()
                .ToList();

И использовать список для выборки значений словаря и группировки:

var dRes = d
            //для каждой записи в словаре выбираем все типы в паре с ключом
            .SelectMany(entry => types.Where(t => HasFlag(entry.Value, t)).Select(t => new {t, entry.Key}))
            //группируем по типу
            .GroupBy(pair => pair.t, pair => pair.Key)
            //переводим в словарь
            .ToDictionary(group => group.Key, group => group.ToList());

Альтернативно, можно сделать выборку из types и для каждого типа выбрать подходящие ключи из словаря.

Демо на ideone: https://ideone.com/OMCtY9

Answer 2

Вот таким образом можно добится требуемого результата

    var enumValues = Enum.GetValues(typeof(GroupType));
    Dictionary<GroupType, List<int>> result = new Dictionary<GroupType, List<int>>();
    foreach (var p in d)
    {
        uint flags = (uint)p.Value;
        foreach (var enumValue in enumValues)
        {
            if ((flags & (int)enumValue) != 0)
            {
                GroupType group = (GroupType)enumValue;
                if (result.ContainsKey(group))
                {
                    List<int> list = result[group];
                    list.Add(p.Key);
                }
                else
                {
                    List<int> list = new List<int>() { p.Key };
                    result.Add(group, list);
                }
            }
        }
    }

Суть: читаем значение из словаря. Далее бежим по всем элементам перечисления GroupType и проверяем, имеется ли флаг перечисления в текущем значении(для этого делаем операцию &, если флаг не установлен, то битовая операция И вернет 0, иначе != 0). В случае, если перечисление имеется в существующем значении, то добавляем элемент в результирующий словарь.

READ ALSO
Как выполнять task в отдельном потоке?

Как выполнять task в отдельном потоке?

Есть метод, который читает журнал событий:

193
Собрать универсальное C# приложение

Собрать универсальное C# приложение

Недавно сделал для себя открытие - при сборкеNET-приложения я могу выбрать разрядность целевой платформы

174
C# Ошибка в decrypt

C# Ошибка в decrypt

Как справиться с ошибкой

191
Wordpress не удается подключить стили

Wordpress не удается подключить стили

Все перепроверил несколько разНе знаю где ошибка

197