Представляющая отношения между устройствами, когда представлен только один-ко-многим сопоставления


Есть устройства, которые взаимодействуют друг с другом. Уже существующие в наследство codebase-это список один-ко-многим сопоставления между устройствами. Я написал функцию в мой бэкэнд, который обрабатывает этот список. Каждое преобразование выполняется в виде специального объекта, в котором хранится исходный объект, так и несколько направлений, что источник сопоставляется.

В передний конец, у меня есть список устройств, как флажки. Когда я выберите устройство, нужно найти для каждого устройства, все остальные устройства, это касается, как, действуя в качестве отправителя, и в качестве приемника. Затем мне нужно проверить, как хорошо эти коробки.

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

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

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

public static List<Dictionary<string, List<string>>> GetDependentDeviceMasterSet(Dictionary<CustomDeviceStruct, List<dataTag>> externalMappingList)
{
    var sourceDestinationMappings = new Dictionary<string, List<string>>();
    var destinationSourceMappings = new Dictionary<string, List<string>>();           
    var mappings = new List<Dictionary<string, List<string>>>();      

    foreach (var externalMapping in externalMappingList)
    {
        var sourceName = externalMapping.Key.deviceName;
        if (sourceDestinationMappings.ContainsKey(sourceName))
        {
            foreach (var data in externalMapping.Value)
            {
                var destinationName = data.deviceName;
                if((sourceDestinationMappings[sourceName]).Contains(destinationName) == false)
                { 
                    (sourceDestinationMappings[sourceName]).Add(destinationName);
                    if (destinationSourceMappings.ContainsKey(destinationName))
                    {
                        foreach (var includedSourceName in destinationSourceMappings[destinationName])
                        {
                            if (destinationSourceMappings[destinationName].Contains(includedSourceName) == false)
                            {
                                destinationSourceMappings[destinationName].Add(includedSourceName);
                            }
                        }
                    }
                }                        
            }
        }
        else
        {
            var destinations = new List<string>();
            foreach (var data in externalMapping.Value)
            {
                var destinationName = data.deviceName;
                destinations.Add(destinationName);
                if (destinationSourceMappings.ContainsKey(destinationName))
                {
                    if (destinationSourceMappings[destinationName].Contains(sourceName) == false)
                    {
                        destinationSourceMappings[destinationName].Add(sourceName);
                    }
                }
                else
                {
                    var sources = new List<string>();
                    sources.Add(sourceName);
                    destinationSourceMappings.Add(destinationName, sources);                        
                }
            }
            sourceDestinationMappings.Add(sourceName, destinations);
        }
    }
    mappings.Add(sourceDestinationMappings);
    mappings.Add(destinationSourceMappings);
    return mappings;
}

И в переднего плана, я обрабатываю его так :

private void FillAllNodesThatAreRelatedTo(TreeNode selectedDeviceNode)
{  
    foreach(TreeNode childNode in selectedDeviceNode.Parent.Nodes)
    {
        if (childNode.Text == selectedDeviceNode.Text)
        {
            continue;
        }

        if (this.dependentDeviceMasterSet[0].ContainsKey(selectedDeviceNode.Text))
        {
            var sourceDestinationMapping = this.dependentDeviceMasterSet[0][selectedDeviceNode.Text];
            if (sourceDestinationMapping.Contains(childNode.Text))
            {
                childNode.Checked = true;
                continue;
            }
            else
            {
                childNode.Checked = false;
                continue;
            }    
        }               

        if (this.dependentDeviceMasterSet[1].ContainsKey(selectedDeviceNode.Text))
        {
            var destinationSourceMapping = this.dependentDeviceMasterSet[1][selectedDeviceNode.Text];
            if (destinationSourceMapping.Contains(childNode.Text))
            {
                childNode.Checked = true;
                continue;
            }
            else
            {
                childNode.Checked = false;
                continue;
            }
        }
        if ((this.dependentDeviceMasterSet[0].ContainsKey(selectedDeviceNode.Text) == false) &&
            (this.dependentDeviceMasterSet[1].ContainsKey(selectedDeviceNode.Text) == false))
        {
            childNode.Checked = false;
            continue;
        }
    }
}

На первой строке код, используя LINQ, что я до сих пор пытался упростить это:

var mappingsThatContainSource = from mapping in externalMappingList
                                where sourceDestinationMappings.ContainsKey(mapping.Key.deviceName)
                                select mapping;

Но я не уверен, если я на правильном пути. Известные проблемы:

  1. Слишком много вложенности. Поэтому хочу попробовать в LINQ.
  2. Неэффективное использование структур данных


Комментарии
1 ответ

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


А Dictionary<string, List<string>> Я могу понять, но List<Dictionary<string, List<string>>> мой глаз дергался неудержимо. ;-)

Даже Dictionary<CustomDeviceStruct, List<dataTag>> странно мне: почему не List<dataTag> просто собственность CustomDeviceStruct?


Не использовать ContainsKeyиспользуйте TryGetValue. Таким образом, вы также избежать многократное использование устройств, таких как sourceDestinationMappings[sourceName].


Я не могу понять, что это должен делать:

if (destinationSourceMappings.ContainsKey(destinationName))
{
foreach (var includedSourceName in destinationSourceMappings[destinationName])
{
if (destinationSourceMappings[destinationName].Contains(includedSourceName) == false)
{
destinationSourceMappings[destinationName].Add(includedSourceName);
}
}
}

Как я читал, кажется, что вы перебрать все элементы List<T> чтобы проверить, если каждый элемент из этого списка присутствует в списке, и если нет (а почему это вообще возможно?) вы добавляете его в список.


Давайте попробуем переписать свою логику, которая, к сожалению, переплетаются различные процессы. Начиная с else, который может быть упрощен до этого:

var destinations = externalMapping.Value.Select(x => x.Name).ToList();

foreach (var dataTag in externalMapping.Value)
{
var destinationName = dataTag.Name;
if (!destinationSourceMappings.TryGetValue(destinationName, out List<string> sources))
{
sources = new List<string>();
}

sources.Add(sourceName);
destinationSourceMappings[destinationName] = sources.Distinct().ToList();
}

sourceDestinationMappings.Add(sourceName, destinations);

Но это все еще кажется загроможденным и чересчур подробно, что он делает. Давайте переделаем, что некоторые, например, путем перемещения destinations ассигнационный до конца и некоторые другие мелкие улучшения:

foreach (var destinationName in externalMapping.Value.Select(x => x.deviceName))
{
if (!destinationSourceMappings.TryGetValue(destinationName, out List<string> sources))
{
sources = new List<string>();
}

sources.Add(sourceName);
destinationSourceMappings[destinationName] = sources.Distinct().ToList();
}

sourceDestinationMappings.Add(sourceName,
externalMapping.Value.Select(x => x.deviceName).ToList());

Откровенно говоря, это все еще не удовлетворяет меня. Он чувствует себя некрасиво. Но, по крайней мере, это гораздо более читабельным, что оригинал, потому что вам не нужно для разбора 20+ строк, чтобы обнаружить, что две отдельные процессы происходят внутри else.

Кстати, если sources был поиска HashSet, вы даже не нужно делать sources.Distinct().ToList();


При взгляде на if и логика в целом, я обнаружил, что он может быть переписан следующим образом:

var sourceDestinationMappings = externalMappingList
.ToDictionary(x => x.Key.deviceName,
x => x.Value.Select(dataTag => dataTag.deviceName).ToList());

foreach (var externalMapping in externalMappingList)
{
var sourceName = externalMapping.Key.deviceName;

if (!sourceDestinationMappings.TryGetValue(sourceName, out List<string> destinations))
{
destinations = new List<string>();

foreach (var destinationName in externalMapping.Value.Select(x => x.deviceName))
{
if (!destinationSourceMappings.TryGetValue(destinationName, out List<string> sources))
{
sources = new List<string>();
}

sources.Add(sourceName);
destinationSourceMappings[destinationName] = sources.Distinct().ToList();
}
}

destinations.AddRange(externalMapping.Value.Select(x => x.deviceName));
sourceDestinationMappings[sourceName] = destinations.Distinct().ToList();
}

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

var sourceDestinationMappings = externalMappingList
.ToDictionary(x => x.Key.deviceName,
x => x.Value.Select(dataTag => dataTag.deviceName).ToList());

foreach (var externalMapping in externalMappingList)
{
var sourceName = externalMapping.Key.deviceName;

foreach (var destinationName in externalMapping.Value.Select(dataTag => dataTag.deviceName))
{
if (!destinationSourceMappings.TryGetValue(destinationName, out List<string> sources))
{
sources = new List<string>();
}

sources.Add(sourceName);
destinationSourceMappings[destinationName] = sources.Distinct().ToList();
}
}


Честно говоря, я не уверен, что это решение 100% правильное. Вы должны проверить это самостоятельно: возьмите тот же набор данных и применить свой код и этот код и посмотреть, если результаты для различных сценариев. И даже если они это сделают, не просто копипастить то, что я написал: стараюсь следовать, какие изменения я сделал и почему я так поступил.

Сравните, например, два последних кодовых блоков: посмотреть destinationsи обратите внимание, как в предыдущей версии ничего не происходит, что не могут быть отменены.
- Мы берем Value из sourceDestinationMappings,
- мы тогда добавить externalMapping.Value.Select(x => x.deviceName) к существующим destinations,
- а затем мы удаляем повторяющиеся записи, когда мы назначаем destinations обратно sourceDestinationMappings[sourceName].

Но все это уже произошло в первой строке:

var sourceDestinationMappings = externalMappingList
.ToDictionary(x => x.Key.deviceName,
x => x.Value.Select(dataTag => dataTag.deviceName).ToList());

Но externalMappingList ничего не изменилось с тех пор, поэтому все, что логика не имеет смысла. И таким образом, вы можете удалить его, что означает, что вы в конечном итоге с окончательной версией.


Как для кода пользовательского интерфейса, сюда входят:


  • Извлечение selectedDeviceNode.Text один раз и хранить его в Ясной имя переменной, если вы собираетесь использовать его несколько раз.

  • Не использовать ContainsKey когда вам нужно использовать результат, если ключ присутствует. Даже хуже: вы используете ContainsKey дважды!

  • Та же логика применяется к dependentDeviceMasterSet[0] и dependentDeviceMasterSet[1]так это, очевидно, должны были перенести в отдельный метод.

И тогда вы в конечном итоге с чем-то вроде этого:

private void FillAllNodesThatAreRelatedTo(TreeNode selectedDeviceNode)
{
foreach(TreeNode childNode in selectedDeviceNode.Parent.Nodes)
{
if (childNode.Text == selectedDeviceNode.Text)
{
continue;
}

var sourceContains = CheckDevice(childNode, 0);
var destinationContains = CheckDevice(childNode, 1);

if (!sourceContains && !destinationContains)
{
childNode.Checked = false;
}
}
}

private bool CheckDevice(TreeNode childNode, int setId)
{
if(this.dependentDeviceMasterSet[setId].TryGetValue(childNode.Text, out List<string> mapping)
{
childNode.Checked = mapping.Contains(childNode.Text);
return true;
}

return false;
}

Это не было протестировано, это было написано в блокноте++. Смотрите на это как пример повторного использования кода.

(Кроме того, что за передний конец этого кода? ASP.NET -форм? Разве нет лучшего способа, чтобы связать таблицу с данными, а не зацикливание через что-то?)

1
ответ дан 2 февраля 2018 в 01:02 Источник Поделиться