Найти ближайшие ключи в KeyValuePair (C #) - PullRequest
0 голосов
/ 04 июля 2019

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

{1, "ISO"}
{2, "AEA"}
...
{256, "OI"}
...
{302, "OI"}
{303, "N2"}
{304, "N2.5"}
...
{400, "N2"}

Цель этого - найти пары значений ключа, которые содержат значения в определенном порядке, и вернуть ключ LOWEST из этих пар значений ключа. Чтобы получить то, что я чувствовал на полпути, я извлек все возможности пар, которые содержат значения «OI», «N2» и «N2.5».

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

Dictionary map = new Dictionary<int,string>();

map.Add(new KeyValuePair<int, string>(256, "OI");
map.Add(new KeyValuePair<int, string>(302, "OI");
map.Add(new KeyValuePair<int, string>(303, "N2");
map.Add(new KeyValuePair<int, string>(304, "N2.5");
map.Add(new KeyValuePair<int, string>(400, "N2");

Я хотел бы извлечь элементы из этого словаря, ключ которого ближе всего к другому, чтобы я получил список в порядке возрастания, например, KeyValuePairs, содержащие 302, 303 и 304 в качестве ключей. Какой лучший способ пойти по этому поводу?

Ответы [ 2 ]

1 голос
/ 04 июля 2019

Вот простая быстрая версия с подробным объяснением, если вы хотите учиться у нее одновременно.

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

Dictionary<int, string> map = new Dictionary<int, string>();

map.Add(256, "OI");
map.Add(302, "OI");
map.Add(303, "N2");
map.Add(304, "N2.5");
map.Add(400, "N2");

// will contain the final results.
Dictionary<int, string> results = new Dictionary<int, string>();

// get map ordered
var mapList = map.OrderBy(o => o.Key).ToList();

// iterate until we have worked out each values
while (mapList.Any())
{
    // take first item
    var item = mapList[0];

    // kepp the index of found consecutive values                             
    var index = 0;

    // loop for each value
    for (int i = 1; i < mapList.Count; i++)
    {
        // if the value isn't 1 higher get out the loop
        if (mapList[i].Key != mapList[index].Key + 1)
        {
            break;
        }
        else
        {
            // value is more than 1 so keep going until we can't
            index++;
        }
    }

    // if we have found at least 2 consecutive numbers
    if (index > 0)
    {
        // add the first of the sequence
        results.Add(item.Key, item.Value);

        // add the rest of the sequence that was found
        for (int i = 0; i < index; i++)
        {
            results.Add(mapList[i + 1].Key, mapList[i + 1].Value);
        }

        // finally removed all items found plus the starting item (hence the + 1)
        mapList.RemoveRange(0, index + 1);
    }
    else
    {
        // no consecutive numbers found so just remove the item so we can loop and try the next one
        mapList.RemoveAt(0);
    }
}
1 голос
/ 04 июля 2019

А вот и мы:

    Dictionary<int, string> map = new Dictionary<int, string>();

    map.Add(256, "OI");
    map.Add(302, "OI");
    map.Add(303, "N2");
    map.Add(304, "N2.5");
    map.Add(400, "N2");
    var minDiffGroup = map.SelectMany(item1 =>
         map
         .Where(item2 => !object.Equals(item1, item2))
         .Select(item2 => new { Diff = Math.Abs(item1.Key - item2.Key), Item1 = item1, Item2 = item2 })
      )
      .GroupBy(item => item.Diff)
      .OrderBy(group => group.Key)
      .FirstOrDefault();

    Console.WriteLine("Diff: {0}", minDiffGroup.Key);
    foreach (var item in minDiffGroup)
        Console.WriteLine("Item 1: {0}\tItem 2:{1}", item.Item1, item.Item2);
    Console.ReadKey();

Вывод:

Diff: 1
Item 1: [302, OI]       Item 2:[303, N2]
Item 1: [303, N2]       Item 2:[302, OI]
Item 1: [303, N2]       Item 2:[304, N2.5]
Item 1: [304, N2.5]     Item 2:[303, N2]

Я бы помог в дальнейшем, но ваш вопрос очень расплывчатый.

Редактировать

Хорошо, после того, как вы добавили дополнительную информацию, я изменил свое выражение лямда:

    Dictionary<int, string> map = new Dictionary<int, string>();

    map.Add(256, "OI");
    map.Add(302, "OI");
    map.Add(303, "N2");
    map.Add(304, "N2.5");
    map.Add(400, "N2");
    var orderedMap = map.OrderBy(item => item.Key); // Order the map
    var minDiffGroup = orderedMap.SelectMany(item1 =>
         orderedMap
         .SkipWhile(item2 => !object.Equals(item1, item2)) //skip all elements which were already merged
         .Skip(1) //skip item1 == item2
         .Select(item2 => new { Diff = Math.Abs(item1.Key - item2.Key), Item1 = item1, Item2 = item2 }) //create an unknown type with Key = diff and both items
      )
      .GroupBy(item => item.Diff) //group by Diff
      .OrderBy(group => group.Key) //order by Diff
      .FirstOrDefault(); //Take the smallest group

    if (minDiffGroup?.Count() > 0)
    {
        var lowestChain = minDiffGroup
             .OrderBy(item => item.Item1.Key) //order by the key of item1
             .TakeWhile(item => item.Item1.Key + minDiffGroup.Key == item.Item2.Key) //take all items as long as the next item has the difference of this group (otherwise there is a gap)
             .SelectMany(item => new List<KeyValuePair<int, string>>() { item.Item1, item.Item2 }) //select all collected KeyValuePairs
             .Distinct(); //take every once
        foreach (var item in lowestChain)
        {
            Console.WriteLine(item);
        }
    }

Вывод:

[302, OI]
[303, N2]
[304, N2.5]
...