Что такое сборный эквивалент многомерного массива?

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

001 001 One
001 002 Two
001 003 Three

002 001 One
002 002 Two
002 003 Three

...

Теперь, конечно, я мог бы создать массив строки [x] [y] = z, но этот массив должен быть изменен, и я бы предпочел использовать строковые представления индексаторов, чем преобразовывать в числовые. Причина в том, что мне нужно будет искать данные по строке, и я не вижу смысла в ненужных преобразованиях строка-> числа.

Моя первая мысль была такой:

Dictionary<string, Dictionary<string, string>> data;

data = new Dictionary<string, Dictionary<string, string>>();

Dictionary<string, string> subdata = Dictionary<string, string>();

subdata.Add(key, string);
data.add(key2, subdata);

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

Итак, как лучше всего хранить такие данные в коллекции?

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


person Erik Funkenbusch    schedule 23.07.2009    source источник
comment
Вы сказали, что они могут быть непоследовательными, но всегда ли они упорядочены? Это влияет на ваш выбор списка или словаря, хотя вы всегда можете иметь перечислитель, который выполняет сортировку перед перечислением.   -  person Robert Paulson    schedule 23.07.2009
comment
Список можно индексировать только последовательно. Если вы удалите элемент из списка, тогда элемент + 1 станет элементом - 1 в индексе, поэтому это не сработает. Они должны быть проиндексированы по их значениям.   -  person Erik Funkenbusch    schedule 23.07.2009
comment
Я вижу, что многие ваши комментарии касаются того, чтобы не писать оболочку «для обслуживания». Вам следует пересмотреть эту позицию, потому что вы, вероятно, обнаружите, что написание класса-оболочки упрощает кодирование, тестирование и поддержку в долгосрочной перспективе, а не наоборот. Инкапсуляция - отличное средство объектно-ориентированного программирования. В противном случае вы в конечном итоге протолкнете всю логику и добавите ненужную привязку к потребительскому коду, а также жестко объедините хранилище и реализацию. Напишите свои тестовые примеры, прежде чем писать оболочку, и будет совершенно очевидно, какой интерфейс вам нужно поддерживать.   -  person Robert Paulson    schedule 23.07.2009
comment
Моя позиция по поводу кода оболочки выходит далеко за рамки чисто технических соображений. Я придерживаюсь принципа DRY, и я считаю, что код оболочки довольно часто нарушает этот принцип (даже если вы не повторяете код внутри, вы повторяете шаблон). Я предпочитаю решения, в которые мне не нужно оборачиваться. Например, решение Райана вообще не требует кода оболочки и дает тот же результат. Я считаю это лучшим решением.   -  person Erik Funkenbusch    schedule 23.07.2009
comment
Кроме того, много работы и усилий было вложено в классы коллекции BCL, а их методы доступа хорошо изучены и (надеюсь) не содержат ошибок. Если мне придется воссоздать большую часть этого кода, простая статистика скажет, что я не буду создавать его где-то рядом, если я не потрачу столько же времени на их проектирование. Очень легко превратить то, что O (1), в O (n), O (n ^ 2) или O (log n) или хуже, если вы не обращаете внимания. Кроме того, когда вы инкапсулируете что-то с большим количеством функций, например Dictionary, вам придется написать много оболочек, если вам нужна эта функциональность.   -  person Erik Funkenbusch    schedule 23.07.2009
comment
Принуждение пользователей вашей библиотеки к передаче Dictionary ‹string, Dictionary‹ string, string ›› не принесет им никакой пользы, особенно когда они привязывают их код к вашей реализации структуры данных. Хорошие методы объектно-ориентированного проектирования говорят, что вы этого не делаете. Создаваемый вами класс должен по-прежнему использовать общие классы коллекций внутри, так что вы не повлияете на характеристики большого O и вряд ли внесете ошибки. IList ‹T› - это что-то вроде 9 методов. На днях я сделал обертку, потому что я ввел в свой список другую семантику, и это заняло у меня все 10 минут, включая модульные тесты.   -  person Robert Paulson    schedule 26.07.2009


Ответы (6)


Это довольно распространенный запрос, и большинство людей в конечном итоге пишут некоторые вариации класса Tuple. Если вы используете ASP.Net, вы можете использовать уже доступный класс Triple, в противном случае напишите что-нибудь вроде:

public class Tuple<T, T2, T3>
{
    public Tuple(T first, T2 second, T3 third)

    {
        First = first;
        Second = second;
        Third = third;
    }

    public T First { get; set; }
    public T2 Second { get; set; }
    public T3 Third { get; set; }

}

Существует общий класс из трех кортежей, поэтому вы можете создать новый List<Tuple<string, string, string>>(), создать свои кортежи и добавить их. Расширьте этот базовый класс с помощью некоторых функций индексации, и все готово.

Изменить: список со словарем не кажется правильным, потому что каждый словарь содержит только одно значение. Между ключом и значениями нет связи с несколькими записями - есть просто один ключ, состоящий из нескольких частей, и одно связанное с ним значение. Данные эквивалентны строке базы данных (или кортежу!).

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

    public class MyTupleList : List<Tuple<string, string, string>>
    {
        public Tuple<string, string, string> this[string first, string second]
        {
            get
            {
                return (this.Find(x => x.First == first && x.Second == second));
            }
            set
            {
                this[first, second] = value;
            }
        }
    }
person womp    schedule 23.07.2009
comment
Я думал об этом, но я не мог найти хороший способ поиска элементов без просмотра всего списка и сравнения значений. Вы можете сделать это с LINQ, но я не уверен, что это всегда будет использоваться с C # 3 или выше. Это означало бы, что мне пришлось бы добавить весь поисковый код поиска, который, как вы, вероятно, знаете, нелегко исправить. - person Erik Funkenbusch; 23.07.2009
comment
Через минуту я дам вам полный пример, он вам понравится;) - person womp; 23.07.2009
comment
Спасибо за хороший пример. Но, как я сказал в своем комментарии, я не могу быть уверен, что он всегда будет использоваться с C # 3, поэтому LINQ, хотя и является хорошим решением проблемы, не подходит. И хотя это интересное упражнение, я действительно не хочу тратить свое время на написание хорошего кода индексации, этого должны помочь нам существующие классы коллекций;) - person Erik Funkenbusch; 23.07.2009
comment
Ну, это зависит от того, насколько гибким вам нужен поиск ключей. На самом деле это просто общая версия того, что предлагают Райан и Джон, которые для ваших целей, вероятно, являются лучшими ответами здесь, пример индексации просто ледяной, в этом даже нет необходимости. И без LINQ сделать то же самое просто;) - person womp; 23.07.2009
comment
Думаю, я все равно дам вам ответ, потому что это хорошее решение. - person Erik Funkenbusch; 23.07.2009
comment
вам нужен код для компиляции в разных версиях компилятора или для работы в разных версиях среды выполнения? Скомпилированные библиотеки DLL / программы, скомпилированные на C # 3, работают в среде выполнения v2. Linq синтаксически подсказывает и просто компилируется в обычные вызовы статических методов. (Все методы расширения представляют собой синтаксический сахар, скомпилированный в вызовы статических методов) - person Rune FS; 23.07.2009
comment
Компилировать на разных версиях компилятора. Это общий по определению, поэтому он должен иметь возможность создавать типы во время компиляции. - person Erik Funkenbusch; 23.07.2009

Я думаю, это действительно зависит от того, что вы здесь моделируете. Если вы планируете использовать объектно-ориентированный подход, вам не следует думать об этом как о произвольных элементах внутри структуры данных.

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

struct Key {
   public int Val1 { get; set; }
   public int Val2 { get; set; }
}

....

Dictionary<Key, string> values;

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

person Ryan Brunner    schedule 23.07.2009
comment
Я не уверен, что это делает то, что вы намереваетесь. Или, может быть, потому, что вы используете структуру (тип значения). Все ли key.Val1, key.Val2 равны, если они содержат одинаковые данные? - person Erik Funkenbusch; 23.07.2009
comment
Мне нравится такой подход. Возможно, вам понадобится реализовать IComparer или Equals. - person Randolpho; 23.07.2009
comment
То есть вернется ли это к истине? новый ключ {Val1 = 1, Val2 = 2} == новый ключ {Val1 = 1, Val2 = 2}; - person Erik Funkenbusch; 23.07.2009
comment
Да, извините - вам нужно будет реализовать IComparer и equals. Я не уверен на 100% в ключевой оценке, но уверен, что это сработает. Требуется тестирование и все такое прочее. - person Ryan Brunner; 23.07.2009
comment
Этот подход очень похож на подходы, указанные мной и dtb в наших редакциях, но он использует структуру, а не конкатенацию строк. Приведенный выше пример неполный; IComparer и / или Equals необходимо реализовать / переопределить, но в остальном это очень надежный подход. - person Randolpho; 23.07.2009
comment
GetHashcode, вероятно, также придется переопределить, учитывая тот факт, что Dictionary, вероятно, будет использовать хеш-таблицу внутри. - person Randolpho; 23.07.2009
comment
Да, это похоже на лучшее решение, чем ваш подход к инкапсуляции в новом классе. Он решает проблему получения доступа ко всем функциям словаря без написания большого количества кода оболочки. - person Erik Funkenbusch; 23.07.2009

Учитывая подходящий Pair<A,B> класс *, оставлен в качестве упражнения для читателя , вы можете использовать Dictionary<Pair<string, string>, string>.

* Класс с равенством и переопределением хэш-кода, ничего особо сложного.

person John Kugelman    schedule 23.07.2009
comment
для этого конкретного вопроса можно использовать Point (docs.microsoft. com / en-us / dotnet / api / system.drawing.point) вместо его создания, поэтому он становится Dictionary ‹Point, SomeClass› () ... Point - это структура из двух двойников, извлекаемая как: dictionary [новая точка (x, y)] - person A. Harkous; 29.04.2020

Подойдет ли вам List<List<T>>? По-прежнему бестолково, но лучше словарей ИМХО.


РЕДАКТИРОВАТЬ: А как насчет Dictionary<string,string> и сопоставления двух ключей с одной строкой?

var data = new Dictionary<string,string>(StringComparer.Ordinal);

data[GetKey("002", "001")] = "One";

с участием

string GetKey(string a, string b) {
    return a + "\0" + b;
}
person dtb    schedule 23.07.2009
comment
Нет. Как я уже сказал, я бы предпочел индексировать их по строкам. И они не всегда могут быть последовательными (в середине может быть пропущенный номер). - person Erik Funkenbusch; 23.07.2009
comment
О, хорошо ... Я думаю, что ваше упоминание о многомерных массивах нас сбило с толку (поскольку они также не были бы проиндексированы строками). - person jsight; 23.07.2009

List<List<string>> - действительно ваш лучший выбор в этом случае. Но я согласен, это глупо. Лично я бы создал собственный класс, который реализует двумерный индексатор и, возможно, использовал бы List<List<T>> внутри.

Например:

public class DynamicTwoDimensonalArray<T>
{
  private List<List<T>> Items = new List<List<T>>();

  public T this[int i1, int i2]
  {
    get
    {
      return Items[i1][i2];
    }
    set
    {
      Items[i1][i2] = value;
    }
  }  
}

Это основная идея, которая поможет вам начать работу; очевидно, что установщику нужно иметь дело с проблемами границ. Но это только начало.

Изменить:

Нет. Как я уже сказал, я бы предпочел индексировать их по строкам. И они не всегда могут быть последовательными (в середине может быть пропущенный номер). - Таинственный человек

Хм ... это интересно. В этом случае лучше всего было бы создать своего рода конкатенацию комбинации двух индексаторов и использовать это в качестве ключа в одноуровневом словаре. Я бы по-прежнему использовал собственный класс, чтобы упростить индексацию. Например:

public class TwoDimensionalDictionary
{
  private Dictionary<string, string> Items = new Dictionary<string, string>();

  public string this[string i1, string i2]
  {
    get
    {
      // insert null checks here
      return Items[BuildKey(i1, i2)];
    }
    set
    {
      Items[BuildKey(i1, i2)] = value;
    }
  }
  public string BuildKey(string i1, string i2)
  {
    return "I1: " + i1 + " I2: " + i2;
  }  
}
person Randolpho    schedule 23.07.2009
comment
Это интересное решение, но я вижу некоторые проблемы с крайними случаями. Однако их можно решить. Например, если i1, i2 не существует, генерируется исключение. нет возможности получить доступ к функции Try или совершить сделку типа if (Items.Keys.Exists (..)). так что это потребует написания большого количества кода оболочки, которого я стараюсь избегать по причинам технического обслуживания. - person Erik Funkenbusch; 23.07.2009
comment
Совершенно верно. Как я уже сказал, это начало, а не решение. Тем не менее, у вас будут те же проблемы, о которых вы упомянули, даже если вы пойдете по маршруту Dictionary ‹string, Dictionary‹ string, string ››. Использование специального класса для обработки всего этого сделает ваш код более удобным в сопровождении. - person Randolpho; 23.07.2009

Если вам когда-нибудь понадобится найти z по заданному (x,y) (а не, например, найти все y по заданному x), используйте это:

Dictionary<KeyValuePair<string, string>, string>

В противном случае ваш словарь в порядке.

person Pavel Minaev    schedule 23.07.2009