Dictionary<X, Dictionary<Y, Z>>
От: _NN_ www.nemerleweb.com
Дата: 17.10.15 19:15
Оценка:
Стал замечать , что нужно довольно часто иметь такую структуру данных.
Использование простого Dictionary не очень удобно, т.к. добавление или изменение требует нескольких TryGetValue и Add в случае если нет элементов.
Dictionary<Y,Z> yz;
if (!d.TryGetValue(x, out yz)) { yz = d[x] = new Dictionary<Y,Z>(); }

Z z;
if (!yz.TryGetValue(y, out z)) { z = yz[y] = new Z(); }

z.CallMethod();


На данный момент добавились методы расширения GetOrAdd и код легко переписывается в :
d.GetOrAdd(x, _ => new Dictionary<Y,Z>()).GetOrAdd(y, _ => new Z()).CallMethod();


Однако это начинает утомлять.
В итоге подумываю над созданием более удобного класса NestedDictionary<K,V,W> и возможно далее NestedDictionary<K,V,W,X> ...

Что делаете в таких случаях ?
Есть ли готовый вариант для этого ?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Dictionary<X, Dictionary<Y, Z>>
От: agat50  
Дата: 17.10.15 19:21
Оценка:
Здравствуйте, _NN_, Вы писали:

Можно использовать Tuple как ключ. http://stackoverflow.com/questions/955982/tuples-or-arrays-as-dictionary-keys-in-c-sharp
Re[2]: Dictionary<X, Dictionary<Y, Z>>
От: _NN_ www.nemerleweb.com
Дата: 17.10.15 19:25
Оценка:
Здравствуйте, agat50, Вы писали:

A>Здравствуйте, _NN_, Вы писали:


A>Можно использовать Tuple как ключ. http://stackoverflow.com/questions/955982/tuples-or-arrays-as-dictionary-keys-in-c-sharp


Тогда сама цель словаря по отдельным ключам пропадает.
В случае с кортежами для удаление ключа X придётся вместо простого d.Remove(x) городить код.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: Dictionary<X, Dictionary<Y, Z>>
От: agat50  
Дата: 17.10.15 19:30
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Здравствуйте, agat50, Вы писали:


A>>Здравствуйте, _NN_, Вы писали:


A>>Можно использовать Tuple как ключ. http://stackoverflow.com/questions/955982/tuples-or-arrays-as-dictionary-keys-in-c-sharp


_NN>Тогда сама цель словаря по отдельным ключам пропадает.

_NN>В случае с кортежами для удаление ключа X придётся вместо простого d.Remove(x) городить код.

Тогда мб стоит свое расширение для такого вида словарей написать, GetOrAddNew(firstKey,secondKey,...).
Re: Dictionary<X, Dictionary<Y, Z>>
От: Sinix  
Дата: 17.10.15 22:10
Оценка: +2
Здравствуйте, _NN_, Вы писали:

_NN>Что делаете в таких случаях ?

Решаю исходную проблему — переусложнённый код. NestedDictionary<K,V,W,X> — словарь словарей словарей? Серьёзно?

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

Вариантов решения много, стандартные:
* Если класс используется для хранения данных (читай, ссылка на словарь в поле) — не ленимся и заводим типизированную коллекцию (как правило, наследника от KeyedCollection<>).
* Для промежуточных вещей, которые не уходят за границы метода, за глаза хватает словаря по составному ключу.
* Для математики — SparseMatrix из Math.Net или отсюда и т.д.
Re[2]: Dictionary<X, Dictionary<Y, Z>>
От: _NN_ www.nemerleweb.com
Дата: 18.10.15 04:16
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, _NN_, Вы писали:


_NN>>Что делаете в таких случаях ?

S>Решаю исходную проблему — переусложнённый код. NestedDictionary<K,V,W,X> — словарь словарей словарей? Серьёзно?

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


Ну скажем нужен словарь на процесс -> поток -> данные.
И вот получаем Dictionary<int, Dictioary<int, Data>>.

Что взамен предлагается ? Dictionary<Tuple<int,int>,Data>?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Dictionary<X, Dictionary<Y, Z>>
От: Ромашка Украина  
Дата: 18.10.15 05:49
Оценка:
Здравствуйте, _NN_, Вы писали:
_NN>В итоге подумываю над созданием более удобного класса NestedDictionary<K,V,W> и возможно далее NestedDictionary<K,V,W,X> ...

И в чем проблема? Что-нибудь типа такого?
    public class MyDictionary<X, Y> : Dictionary<X, Y>
        where Y : new()
    {
        public new Y this[X index]
        {
            get
            {
                if (!base.ContainsKey(index))
                    base.Add(index, new Y());
                return base[index];
            }
        }
    }

    public class NestedDictionary<X, Y, Z> : MyDictionary<X, MyDictionary<Y, Z>>
        where Z : new()
    { }

    //использование
    nestedDictionary[x][y].CallMethod();


Можешь еще красивый двойной индексер себе дописать, если лень скобки ставить.

_NN>Что делаете в таких случаях ?


Используем ООП. Dictionary<X, Dictionary<Y, Z>> по глупому же выглядит, нет?
http://brunets.name/ua_developer.gif

Всё, что нас не убивает, ещё горько об этом пожалеет.
Re[2]: Dictionary<X, Dictionary<Y, Z>>
От: _NN_ www.nemerleweb.com
Дата: 18.10.15 05:50
Оценка: +2
Здравствуйте, Ромашка, Вы писали:

Р>Используем ООП. Dictionary<X, Dictionary<Y, Z>> по глупому же выглядит, нет?


Почему по глупому ?

Как решается такое ? А если будет больше вложенности ?
Создавать коллекцию на каждый уровень ?
http://rsdn.ru/forum/dotnet/6217965.1
Автор: _NN_
Дата: 18.10.15
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: Dictionary<X, Dictionary<Y, Z>>
От: Ромашка Украина  
Дата: 18.10.15 06:16
Оценка:
Здравствуйте, _NN_, Вы писали:
_NN>Почему по глупому ?

Потому что процесс это не только словарь потоков, а еще какие-то свойства/методы. Пиши класс процесса со словарем потоков и кучей всего остального.

_NN>Как решается такое ? А если будет больше вложенности ?


Используй хештаблицу. Хеш посчитать сможешь? Там даже количество вложений не будет иметь значения.
http://brunets.name/ua_developer.gif

Всё, что нас не убивает, ещё горько об этом пожалеет.
Re[2]: Dictionary<X, Dictionary<Y, Z>>
От: Evgeny.Panasyuk Россия  
Дата: 18.10.15 06:19
Оценка: +1
Здравствуйте, Sinix, Вы писали:

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


В C++ STL это работает из коробки:
{
    unordered_map<int, unordered_map<int, int>> map;
    ++map[10][5];
    map[4][6] = 4;;
}
Re[4]: Dictionary<X, Dictionary<Y, Z>>
От: _NN_ www.nemerleweb.com
Дата: 18.10.15 06:19
Оценка:
Здравствуйте, Ромашка, Вы писали:

Р>Здравствуйте, _NN_, Вы писали:

_NN>>Почему по глупому ?

Р>Потому что процесс это не только словарь потоков, а еще какие-то свойства/методы. Пиши класс процесса со словарем потоков и кучей всего остального.


А если мне нужно только список потоков ?
Давай усложним на более реальный пример.

Сессия -> рабочий стол -> процесс -> поток -> данные.

Какое предложение тут будет ?

class Session : KeyValueCollection<int, Desktop> { ... }
class Desktop : KeyValueCollection<int, Process> { ... }
class Process : KeyValueCollection<int, Thread> { ... }
class Thread : KeyValueCollection<int, Data> { ... }
class Data { ... }


И все это вместо простых чисел ?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[5]: Dictionary<X, Dictionary<Y, Z>>
От: Ромашка Украина  
Дата: 18.10.15 06:21
Оценка:
Здравствуйте, _NN_, Вы писали:
_NN>А если мне нужно только список потоков ?

Чем тебе хештаблица не нравится?
http://brunets.name/ua_developer.gif

Всё, что нас не убивает, ещё горько об этом пожалеет.
Re[6]: Dictionary<X, Dictionary<Y, Z>>
От: _NN_ www.nemerleweb.com
Дата: 18.10.15 06:22
Оценка:
Здравствуйте, Ромашка, Вы писали:

Р>Здравствуйте, _NN_, Вы писали:

_NN>>А если мне нужно только список потоков ?

Р>Чем тебе хештаблица не нравится?


Dictionary и есть хештаблица.
Или я чего-то не понимаю ?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[7]: Dictionary<X, Dictionary<Y, Z>>
От: Ромашка Украина  
Дата: 18.10.15 06:55
Оценка: +1
Здравствуйте, _NN_, Вы писали:
_NN>Dictionary и есть хештаблица.
_NN>Или я чего-то не понимаю ?

Ну да. Так и посчитайте один хеш из всех своих параметров и храните в одном dictionary без вложенности.
http://brunets.name/ua_developer.gif

Всё, что нас не убивает, ещё горько об этом пожалеет.
Re: Dictionary<X, Dictionary<Y, Z>>
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 18.10.15 07:25
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Есть ли готовый вариант для этого ?

Посмотри http://rsdn.ru/article/alg/tlsd.xml
Автор(ы): Сергей Смирнов (Serginio1)
Дата: 14.08.2004
Пример реализации двухуровневого массива с помощью нового средства С# — generics. Сравнение производительности различных реализаций сортированных списков.

Там есть InsertOrNavigate
и солнце б утром не вставало, когда бы не было меня
Re[3]: Dictionary<X, Dictionary<Y, Z>>
От: Sinix  
Дата: 18.10.15 07:43
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


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

EP>В C++ STL это работает из коробки:

Ну так коллекции разные
В дотнете бросается исключение, если ключа нет в словаре. Но ничего не мешает написать свой наследник от Dictionary с поведением, аналогичным плюсам. Именно что-то похожее и имел в виду, когда говорил про нехватку слоя абстракций.
Re[3]: Dictionary<X, Dictionary<Y, Z>>
От: Sinix  
Дата: 18.10.15 07:56
Оценка: +1
Здравствуйте, _NN_, Вы писали:


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


_NN>Ну скажем нужен словарь на процесс -> поток -> данные.

_NN>И вот получаем Dictionary<int, Dictioary<int, Data>>.

_NN>Что взамен предлагается ? Dictionary<Tuple<int,int>,Data>?

Как всегда, всё есть в официальных гадлайнах. Начинаем с гипотетического сценария использования:
processes[someId].Thread[threadId];

Т.е. имеем коллекцию процессов, у процесса — свойство с коллекций потоков. Сами коллекции пишутся в одну строчку наследованием от KeyedCollection<,>.

Если с сценарием не угадал, можно попробовать вариант, что подсказал Evgeny.Panasyuk: пишем словарь (или extension метод), поведение: словарь заполняется new TValue(), если ключа нет в словаре. Конечно, придётся следить, чтоб не замусорить словарь ненужными ключами.
Re[5]: Dictionary<X, Dictionary<Y, Z>>
От: xy012111  
Дата: 18.10.15 08:08
Оценка: +2
Здравствуйте, _NN_, Вы писали:

_NN>>>Почему по глупому ?

Р>>Потому что процесс это не только словарь потоков, а еще какие-то свойства/методы. Пиши класс процесса со словарем потоков и кучей всего остального.

_NN>А если мне нужно только список потоков ?

_NN>Давай усложним на более реальный пример.
_NN>Сессия -> рабочий стол -> процесс -> поток -> данные.

Если это и есть ваша задача, то вы не правильно её понимаете. процессы и потоки (thread) одни и те же среди всех сессий. Их идентификаторы не пересекаются — у разных сессий не может быть процессов или потоков с одинаковыми идентификаторами. Поэтому достаточно иметь словарь поток-данные.

Далее, если нужно, мульти-словарь процесс-потоки итэдэ. Это уже делается довольно просто всего лишь несколькими методами расширения.

Если рассуждать об абстрактной задаче, то
_NN>class Session : KeyValueCollection<int, Desktop> { ... }
_NN>class Desktop : KeyValueCollection<int, Process> { ... }
…

не правильно. Нужен тип Session у которого есть свойство — коллекция рабочих столов. В типе рабочего стола — список процессов итиси.
Re[5]: Dictionary<X, Dictionary<Y, Z>>
От: Sinix  
Дата: 18.10.15 08:11
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Давай усложним на более реальный пример.


_NN>Сессия -> рабочий стол -> процесс -> поток -> данные.

_NN>Какое предложение тут будет ?

Всё то же — создавать класс с свойством-коллекцией. Это в одну строчку делается, например
class Session
{
  MyCollection<int, Desktop> Desktops { get; } = MyCollection.Create((Desktop d) => d.DesktopId); // c# 6
}


_NN>И все это вместо простых чисел ?

Разумеется. Потому что твоя гипотетическая простота на практике оборачивается усложнением. Вместо удобной объектной модели ты в одной коллекции хранишь кишки каждого из элементов. Мало того, что это неудобно (легко забыть и перепутать порядок ключей), так ещё и сыпется на простейших задачах вида "добавить имя к процессу".
Re[4]: Dictionary<X, Dictionary<Y, Z>>
От: Evgeny.Panasyuk Россия  
Дата: 18.10.15 08:19
Оценка: +1
Здравствуйте, Sinix, Вы писали:

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

EP>>В C++ STL это работает из коробки:
S>Ну так коллекции разные
S>В дотнете бросается исключение, если ключа нет в словаре.

Это понятно. В STL есть отдельный метод find который не создаёт ничего, а только ищет.
А вообще, вот такое поведение, где operator[] создаёт default в случае отсутствия — очень удобно. Потому что обычно default является нейтральным элементом относительно применяемых операций. Простой пример это подсчёт количества вхождений — ++count[id], или например конкатенация строк result_string[id] += s;, или случай ТС со вложенными контейнерами.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.