Я ищу структуру данных, подобную словарю, который возвращает набор всех связанных объектов к ключу.
Например, я использовал бы его как это:
var data = new FancyDataStructure();
data.Add(new string[] {"Elizabeth", "Liz", "Betty"});
data.Add(new string[] {"Bob", "Robert", "Rob"});
string[] alternateNames1 = data["Betty"];
string[] alternateNames2 = data["Liz"]
В этом экземпляре alternateNames1 был бы массивом, содержащим "Liz" и "Elizabeth", и alternateNames2 будет массивом, содержащим "Elizabeth" и "Betty".
Я не хочу переосмысливать это, но я не мог найти примеры такой структуры.
Обновление
Спасибо тем, которые записали обратно с предложениями. Многие люди предложили использовать некоторую версию Dictionary<string, IEnumerable<string>>
. В настоящее время я использую этот подход, но он на самом деле не выполняет требование, не будучи ужасно трудным поддержать. Каждое значение в каждом списке должно смочь функционировать как ключ к любому значению, когда-либо добавленному к нему в наборе.
Таким образом, учитывая следующее:
data.Add(new string[] {"Elizabeth", "Liz"}
data.Add(new string[] {"Liz", "Betty"}
alternates = data["Betty"];
Я ожидал бы, что альтернативы теперь будут содержать "Elizabeth" и "Liz".
Выглядит, как будто мне, возможно, просто придется создать такую структуру для удовлетворения моим потребностям. Сохраните прибытие идей хотя!
Brian
Ваша проблема звучит так, как будто это действительно проблема построения графиков. Считайте, что имена - это узлы, а принадлежность к множеству - ребра. С этой точки зрения, вам нужна структура данных, которая хорошо справляется с разреженными графами, например список смежности. Это, конечно, похоже на то, что вы уже делаете с Dictionary
, но размышление об этом в таком ключе может привести вас к некоторым полезным реализациям и алгоритмам.
Что-то вроде этого кажется достаточно простым.
var data = new List<string[]>();
data.Add(new string[] {"Elizabeth", "Liz", "Betty"});
data.Add(new string[] {"Bob", "Robert", "Rob"});
var alternateNames1 = data.Where(x =>x.Contains("Betty")).Select(x => x.Where(y => y != "Betty"));
Я бы просто использовал тип Dictionary
. Чтобы построить эту структуру из списка списков, у вас может быть такой код:
var alternateNames = new string[][] {
new string[] { "Elizabeth", "Liz", "Betty" },
new string[] { "Bob", "Robert", "Rob" }, };
var altNameLookup =
(
from nameList in alternateNames
from name in nameList
select new {
Name = name, NameList = nameList.Except(new string[] { name } ) }
).ToDictionary(o => o.Name, o => o.NameList);
Де-факто стандарт alt.net находится в Iesi.Collections, но библиотека базовых классов имеет только HashSet
в dotnet 3.5 или выше.
Я использовал в linq предложения типа "group by", чтобы легко удалять дубликаты из произвольных IEnumerable
коллекций, но это не дает семантики набора.
HashSet<> близок к тому, что вы хотите.
Исходя из ваших требований, я не думаю, что есть что-то готовое, что могло бы сопоставить строки с уже существующими коллекциями; В основном, вам придется написать класс, который принимает метод типа StoreAssociations<
, преобразует IEnumerable в HashSet и итерирует каждый элемент в HashSet, чтобы добавить отображение в IDictionary
на вновь созданный хэшсет.
Как насчет пары структур данных: Dictionary
и Dictionary
Чтобы добавить пару ключей (a, b) [вы можете разложить большее сложение на пары (1 + 2, 2 + 3, ...] действуйте следующим образом: -
Найдите a и b в первом словаре.
Если ни один из них не существует, создайте новый Guid и добавьте (a, g) и (b, g) в первый словарь и (g, List {a}) и (g, List {b}) ко второму словарю.
Если один существует, скажем, a, возьмите из него руководство (g), добавьте другой (b, g) в первый словарь и закрепите b в конце списка, найденного в [g] во втором словаре.
Если оба существуют И у них один и тот же гид - делать нечего.
Если оба существуют и у них разные руководства, вам нужно объединить два набора // Это то, чего не хватает в большинстве других предлагаемых решений, // поэтому выберите Guid, который нужно удалить, возьмите его из второго словаря, добавьте список строк к другой записи, а затем удалите эту запись. Наконец, отметьте все слова в первом словаре, которые были в этом списке.
Чтобы получить все связанные слова, найдите Guid в первом словаре и возьмите список из второго словаря.
Конечно, статическое увеличивающееся длинное значение, вероятно, будет работать лучше, чем Guid.
Я использую следующее:
Она имеет общий тип Set и реализует все прекрасные итераторы, .Contains, .Count и т.д.
Попробуйте использовать словарь, например:
Dictionary<string, List<string>>
Итак, словарь строковых ключей со значениями List
Я написал код, не знаю, насколько он эффективен, но я думаю, что он делает то, что вы хотите.
Это ваша структура
class FancyDataStructure
{
private IDictionary<string, HashSet<string>> dictionary
= new Dictionary<string, HashSet<string>>();
public void Add(params string[] names)
{
HashSet<string> set = new HashSet<string>(names);
for (int i = 0; i < names.Length; i++)
{
if (!dictionary.ContainsKey(names[i]))
{
dictionary.Add(names[i], set);
}
else
{
HashSet<string> union =
new HashSet<string>(set.Union<string>(dictionary[names[i]]));
set = union;
foreach (string oldName in dictionary[names[i]])
{
dictionary[oldName] = union;
}
for (int j = 0; j < i; j++)
{
if (!dictionary.ContainsKey(names[j]))
{
dictionary.Add(names[j], union);
}
}
}
}
}
public string[] this[string key]
{
get
{
List<string> result = dictionary[key].ToList<string>();
result.Remove(key);
return result.ToArray();
}
}
}
, и вы можете использовать ее, например,
static void Main(string[] args)
{
FancyDataStructure data = new FancyDataStructure();
data.Add("Elizabeth", "Liz");
data.Add("Liz", "Betty");
string[] alternates = data["Betty"];
foreach (var item in alternates)
{
Console.WriteLine(item);
}
}
Пространство имен System.Collections.Generic и System.Collections загружены словарями пар ключ-значение, сортированными словарями, объектами List и многим другим.
System.Collections.Generic.Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1, test);
или вложенный список внутри словаря
Dictionary<string, List<string>> dic = new Dictionary<string, List<string>>();
List<string> alternatives = new List<string>();
alternatives.Add("Brenda");
dic.Add("Betty", alternatives);
Просто мысль в другом направлении - строго типизированные наборы данных, кажется, имеют много преимуществ. И сериализованные в виде байтовых массивов, они довольно быстро перемещают многомерно структурированные данные.
Итерация и возможности Linq в некотором роде встроены.
Возможно, это излишне для многих вещей, но у меня есть несколько мест, где я хранил весь набор данных в одном столбце varbinary (max) в SQL.
По сути, у вас есть словарь, в котором несколько ключей отображаются на одно и то же значение. Нет встроенной структуры данных, которая поддерживает нужную вам операцию, но ее легко представить в виде Dictionary {string, HashSet {string}}
в .NET:
static void AddNames(Dictionary<string, HashSet<string>> map, params string[] names)
{
for (int i = 0; i < names.Length; i++)
{
HashSet<string> value;
if (!map.TryGetValue(names[i], out value))
{
value = new HashSet<string>();
map.Add(names[i], value);
}
for (int j = 0; j < names.Length; j++)
{
value.Add(names[j]);
}
}
}
static void Main(string[] args)
{
Dictionary<string, HashSet<string>> names = new Dictionary<string,HashSet<string>>();
AddNames(names, "Chris", "Christopher");
AddNames(names, "Christina", "Chrissy", "Chris");
HashSet<string> relatedToChris = names["Chris"]; // gets "Chris", "Christina", "Chrissy", "Christopher";
HashSet<string> namesRelatedToChristinia = names["Christina"]; // gets "Christina", "Chrissy", "Chris";
}
Вы можете представить свою структуру данных как ориентированный граф, в котором каждый узел имеет ребро, связанное с его именем. Поскольку существует n ^ 2 ребер, словарю требуется время O (n ^ 2) для вставки и памяти. Невозможно сократить время поиска до чего-то лучшего.
К счастью, поскольку он реализован как словарь, поиск по-прежнему O (1). Удаляются O (m), где m - количество значений, связанных с ключом.
Или, поскольку List является ссылочным типом, вы можете сделать следующее ...
Dictionary<string, List<string>> dict = new ...
Выполните следующие действия: -
Чтобы добавить одну ассоциацию (a = b) {разложено из списка эквивалентностей}
Искать a и b в Словаре
Если ни один не существует
dict.Add(a, new List<string>(){a}); dict.Add(b, new List<string>(){b});
Если один существует, скажем, a
var list = dict[a];
list.Add(b);
dict.Add(b, list);
Если оба существуют и списки являются то же самое (сравнение объектов), вы сделали.
Если оба существуют и списки разные:
var list1 = dict[a];
var list2 = dict[b];
list1.AddRange(list2);
dict.Remove(b);
dict.Add(b, list1);