#c#
#c#
Вопрос:
У меня есть 2 словаря, где Number = 0
для всех элементов,
var dict1 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key2", new Test { Number = 0, Name = "Name2" } },
{ "Key3", new Test { Number = 0, Name = "Name3" } }
};
var dict2 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key4", new Test { Number = 0, Name = "Name4" } }
};
Теперь, после устранения повторяющихся пар ключ / значение, в результате комбинированного словаря я хочу установить число = 1, 2, 3, … как это сделать?
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select(grp => grp.First())
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Комментарии:
1.
combine[<SomeKey>].Number = <SomeNumber>;
2. мне нужно иметь для всего элемента
3. Действительно ли ключи имеют номер (Key1 …) или это был просто пример. Вы хотите установить Key1 в Number = 1 …?
4. » мне нужно иметь для всего элемента «. Конечно. Ничто не помешает вам выполнить итерацию по словарю (или ключам словаря) и задать номера для всех элементов. Сделать это было бы намного, намного проще, чем то, что потребовалось бы вам, чтобы придумать конструкцию Linq в вашем вопросе…
5. Словарь неупорядочен, поэтому нет гарантии, что Key1 получит число = 1. Если вы явно не укажете ключ.
Ответ №1:
Вы можете сделать
var n = 0;
а затем сделать это функционально, но не очень эффективно в вашем случае. Функция выберет все элементы из вашего словаря и создаст новую коллекцию с обновленными значениями, которая затем преобразуется в словарь.
var newDict = dict2.Select(d => new Test { Number = n, Name = d.Value[1].Name }).ToDictionary();
Или с помощью старого доброго цикла:
foreach(var d in dict2)
{
d.Value[0].Number = n ;
}
Как предложено в комментарии. Если вы хотите начать с 0, используйте
n ;
если с 1, используйте
n;
Комментарии:
1. Хорошее использование Linq, но вам следует немного объяснить это для тех, кто может этого не понять. Также рассмотрите возможность выполнения
n
вместоn
, поскольку он хочет, чтобы нумерация предположительно начиналась с 1 (или принималаn
1 в качестве начального значения)
Ответ №2:
Попробуйте это:
int i = 0;
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.OrderBy(kvp => kvp.Key)
.ToDictionary(kvp => kvp.Key, kvp => new Test() { Number = i, Name = kvp.First().Value.Name });
Это должно дать вам это:
{ "Key1", new Test { Number = 1, Name = "Name1" } },
{ "Key2", new Test { Number = 2, Name = "Name2" } },
{ "Key3", new Test { Number = 3, Name = "Name3" } }
{ "Key4", new Test { Number = 4, Name = "Name4" } }
Ответ №3:
Я думаю, вы пытаетесь объединить словари, а затем присвоить Number
количество повторяющихся элементов.
Вы могли бы рассмотреть возможность помещения всех словарей в список и перебора каждого элемента и помещения его в объединенный результирующий словарь. Если элемент уже существует в результате, увеличьте Number
свойство.
Начальная настройка:
public class Test
{
public int Number { get; set; }
public string Name { get; set; }
}
var dict1 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key2", new Test { Number = 0, Name = "Name2" } },
{ "Key3", new Test { Number = 0, Name = "Name3" } }
};
var dict2 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key4", new Test { Number = 0, Name = "Name4" } }
};
// Put the dictionaries you want to combine into one list:
var all = new List<Dictionary<string, Test>>();
all.Add(dict1);
all.Add(dict2);
// Declare result dictionary
var combine = new Dictionary<string, Test>();
Настройка завершена, это основной цикл, который вы хотите:
foreach (var dict in all)
{
foreach (var kvp in dict)
{
if (combine.ContainsKey(kvp.Key))
{
combine[kvp.Key].Number ;
}
else
{
combine.Add(kvp.Key, kvp.Value);
}
}
}
Интерактивный вывод оболочки:
Dictionary<string, Submission#0.Test>(4) {
{ "Key1", Submission#0.Test { Name="Name1", Number=1 } },
{ "Key2", Submission#0.Test { Name="Name2", Number=0 } },
{ "Key3", Submission#0.Test { Name="Name3", Number=0 } },
{ "Key4", Submission#0.Test { Name="Name4", Number=0 } }
}
Ответ №4:
Существует перегрузка расширения Select, которое также предоставляет индекс (MSDN)
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select((grp,index) => new { Key = grp.Key, Value = new Test { Number = index 1, Name = grp.First().Name}})
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Ответ №5:
Вы можете перебирать ключи
int n = 1;
foreach (string key in combine.Keys) {
combine[key].Number = n ;
}
Ключи возвращаются не по порядку. Если вы хотите пронумеровать их по порядку:
int n = 1;
var orderedKeyValuePairs = combine
.OrderBy(kvp => kvp.Key, StringComparer.CurrentCultureIgnoreCase);
foreach (var kvp in orderedKeyValuePairs) {
kvp.Value.Number = n ;
}
Обратите внимание, что вы можете получить доступ к Number
подобным образом, только если Test
это ссылочный тип ( class
). Если Test
было struct
, вам пришлось бы переназначить всю структуру, потому что словарь вернул бы копию значения.
Необязательный StringComparer
аргумент позволяет указать различные режимы сравнения строк:
Ordinal, OrdinalIgnoreCase, CurrentCulture, CurrentCultureIgnoreCase
Если вы хотите отсортировать по имени:
int n = 1;
var orderedValues = combine.Values
.OrderBy(v => v.Name, StringComparer.CurrentCultureIgnoreCase);
foreach (var v in orderedValues) {
v.Number = n ;
}
Перебор пар ключ-значение или значений также имеет то преимущество, что вы можете изменять значение напрямую, тогда как при переборе ключей (как в моем первом фрагменте кода) вы должны искать словарь, что менее производительно.
Комментарии:
1.
dict1[key].Number = n ;
Я полагаю, что он просит нет?2. Но ключи — это строки…
OrderBy
Может не дать желаемого результата.3. Ключи — это строки, значения — объекты типа Test {int Number, имя строки}