제목은 충분히 기본적입니다. 내가 할 수없는 이유 :
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
제목은 충분히 기본적입니다. 내가 할 수없는 이유 :
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
답변:
원래 질문에 대한 의견은 이것을 아주 잘 요약합니다.
아무도 그 기능을 설계, 지정, 구현, 테스트, 문서화 및 제공하지 않았기 때문입니다. -@Gabe Moothart
그 이유는? 글쎄요, 딕셔너리 병합의 동작이 프레임 워크 지침에 맞는 방식으로 추론 될 수 없기 때문일 것입니다.
AddRange
데이터 범위가 중복 항목을 허용하므로 범위가 연관 컨테이너에 의미가 없기 때문에 존재하지 않습니다. 예를 들어 IEnumerable<KeyValuePair<K,T>>
해당 컬렉션 이있는 경우 중복 항목을 방지하지 않습니다.
키-값 쌍 모음을 추가하거나 두 사전을 병합하는 동작은 간단합니다. 그러나 여러 중복 항목을 처리하는 방법은 그렇지 않습니다.
중복을 처리 할 때 메서드의 동작은 어떻게됩니까?
내가 생각할 수있는 적어도 세 가지 해결책이 있습니다.
예외가 발생하면 원본 사전의 상태는 어떻습니까?
Add
거의 항상 원자 적 작업으로 구현됩니다. 성공하고 컬렉션의 상태를 업데이트하거나 실패하고 컬렉션의 상태는 변경되지 않은 상태로 유지됩니다. 으로 AddRange
복제 오류로 인해 실패 할 수 있습니다, 방법은 일관성있는 동작을 유지 Add
또한 중복에 예외를 throw하여 원자 확인하고 변경으로 원래 사전의 상태를두고하는 것입니다.
API 소비자로서 중복 요소를 반복적으로 제거해야하는 것은 지루할 것입니다. 이는 모든 중복 값 AddRange
을 포함하는 단일 예외를 throw해야 함을 의미 합니다.
선택은 다음과 같이 요약됩니다.
두 가지 사용 사례를 모두 지원하는 주장이 있습니다. 이를 위해 IgnoreDuplicates
서명에 플래그를 추가 합니까?
IgnoreDuplicates
(true로 설정) 플래그는 기본이되는 구현으로, 상당한 속도를 제공 할 것 중복 검사에 대한 코드 우회.
이제 AddRange
두 경우를 모두 지원할 수 있지만 문서화되지 않은 부작용 이있는 플래그가 있습니다 (프레임 워크 디자이너가 피하기 위해 정말 열심히 노력한 것입니다).
요약
중복을 처리 할 때 명확하고 일관 적이며 예상되는 동작이 없기 때문에 모두 함께 처리하지 않고 시작할 방법을 제공하지 않는 것이 더 쉽습니다.
계속해서 사전을 병합해야하는 경우, 물론 응용 프로그램에 적합한 방식으로 작동하는 사전을 병합하는 자체 확장 메서드를 작성할 수 있습니다.
AddMultiple
과 다른 AddRange
관계없이 구현이 남았습니다 될 것입니다 : 당신의 배열 예외가 발생 함 모든 중복 키? 아니면 처음 발견 한 중복 키에서 예외가 발생합니까? 예외가 발생하면 사전의 상태는 어떻습니까? 깨끗한 것, 아니면 성공한 모든 열쇠?
Add
각각 Add
을 a로 감싸고 try...catch
그런 방식으로 중복을 포착합니다. 또는 인덱서를 사용하고 첫 번째 값을 이후 값으로 덮어 씁니다. 또는을 ContainsKey
시도하기 전에 사용을 사전에 확인하여 Add
원래 값을 유지합니다. 프레임 워크에 AddRange
또는 AddMultiple
메서드 가있는 경우 발생한 일을 전달하는 유일한 간단한 방법은 예외를 통하는 것이며 관련된 처리 및 복구도 그다지 복잡하지 않을 것입니다.
몇 가지 해결책이 있습니다.
Dictionary<string, string> mainDic = new Dictionary<string, string>() {
{ "Key1", "Value1" },
{ "Key2", "Value2.1" },
};
Dictionary<string, string> additionalDic= new Dictionary<string, string>() {
{ "Key2", "Value2.2" },
{ "Key3", "Value3" },
};
mainDic.AddRangeOverride(additionalDic); // Overrides all existing keys
// or
mainDic.AddRangeNewOnly(additionalDic); // Adds new keys only
// or
mainDic.AddRange(additionalDic); // Throws an error if keys already exist
// or
if (!mainDic.ContainsKeys(additionalDic.Keys)) // Checks if keys don't exist
{
mainDic.AddRange(additionalDic);
}
...
namespace MyProject.Helper
{
public static class CollectionHelper
{
public static void AddRangeOverride<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => dic[x.Key] = x.Value);
}
public static void AddRangeNewOnly<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => { if (!dic.ContainsKey(x.Key)) dic.Add(x.Key, x.Value); });
}
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => dic.Add(x.Key, x.Value));
}
public static bool ContainsKeys<TKey, TValue>(this IDictionary<TKey, TValue> dic, IEnumerable<TKey> keys)
{
bool result = false;
keys.ForEachOrBreak((x) => { result = dic.ContainsKey(x); return result; });
return result;
}
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
action(item);
}
public static void ForEachOrBreak<T>(this IEnumerable<T> source, Func<T, bool> func)
{
foreach (var item in source)
{
bool result = func(item);
if (result) break;
}
}
}
}
즐기세요.
ToList()
, 사전은 IEnumerable<KeyValuePair<TKey,TValue>
. 또한 기존 키 값을 추가하면 두 번째 및 세 번째 메서드 메서드가 발생합니다. 좋은 생각이 아닙니다 . 찾고 TryAdd
계십니까? 마지막으로, 두 번째는 교체 할 수 있습니다Where(pair->!dic.ContainsKey(pair.Key)...
ToList()
좋은 해결책이 아니므로 코드를 변경했습니다. try { mainDic.AddRange(addDic); } catch { do something }
세 번째 방법이 확실하지 않은 경우 사용할 수 있습니다 . 두 번째 방법은 완벽하게 작동합니다.
누군가가 나처럼이 질문을 접하게되면 IEnumerable 확장 메서드를 사용하여 "AddRange"를 달성 할 수 있습니다.
var combined =
dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select(grp => grp.First())
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
사전을 결합 할 때 주요 트릭은 중복 키를 처리하는 것입니다. 위의 코드에서는 부분 .Select(grp => grp.First())
입니다. 이 경우 단순히 중복 그룹에서 첫 번째 요소를 가져 오지만 필요한 경우 더 정교한 논리를 구현할 수 있습니다.
dict1
하지 않으면 어떻게 됩니까?
var combined = dict1.Concat(dict2).GroupBy(kvp => kvp.Key, dict1.Comparer).ToDictionary(grp => grp.Key, grp=> grp.First(), dict1.Comparer);
내 생각 엔 무슨 일이 일어 났는지에 대해 사용자에게 적절한 출력이 부족한 것 같습니다. 사전에 반복 키를 가질 수 없기 때문에 일부 키가 교차하는 두 사전 병합을 어떻게 처리할까요? 물론 "I do n't care"라고 말할 수 있지만 이는 false를 반환하거나 반복되는 키에 대한 예외를 던지는 규칙을 위반하는 것입니다.
Add
두 번 이상 발생할 수 있다는 점을 제외 하면을 (를) 호출 할 때 키 충돌이 발생한 위치와 어떻게 다른가요 ? 이 같은 던질 것 ArgumentException
이 Add
확실하지를?
NamedElementException
충돌하는 명명 된 요소를 지정하는 ArgumentException의 innerException 대신 또는 ArgumentException의 innerException으로 throw 되는 새 예외 유형 (아마도 일반적인 옵션은 ?? 일 수 있음)을 만들 수 있습니다 . ... 몇 가지 다른 옵션, 내가 말하고 싶지만
당신은 이것을 할 수 있습니다
Dictionary<string, string> dic = new Dictionary<string, string>();
// dictionary other items already added.
MethodThatReturnAnotherDic(dic);
public void MethodThatReturnAnotherDic(Dictionary<string, string> dic)
{
dic.Add(.., ..);
}
또는 addrange 및 / 또는 위의 패턴을 사용하는 목록을 사용합니다.
List<KeyValuePair<string, string>>
MethodThatReturnAnotherDic
. OP에서 나옵니다. 질문과 내 답변을 다시 검토하십시오.
새 딕셔너리를 처리하는 경우 (그리고 잃을 기존 행이없는 경우) 항상 다른 객체 목록에서 ToDictionary ()를 사용할 수 있습니다.
따라서 귀하의 경우 다음과 같이 할 수 있습니다.
Dictionary<string, string> dic = new Dictionary<string, string>();
dic = SomeList.ToDictionary(x => x.Attribute1, x => x.Attribute2);
Dictionary<string, string> dic = SomeList.ToDictionary...
중복 키가 없을 것임을 알고 있다면 다음을 수행 할 수 있습니다.
dic = dic.Union(MethodThatReturnAnotherDic()).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
중복 키 / 값 쌍이있는 경우 예외가 발생합니다.
왜 이것이 프레임 워크에 없는지 모르겠습니다. 해야한다. 불확실성은 없습니다. 예외를 던지십시오. 이 코드의 경우 예외가 발생합니다.
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);
됩니까?
다음은 C # 7 ValueTuples (튜플 리터럴)를 사용하는 대체 솔루션입니다.
public static class DictionaryExtensions
{
public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> source, IEnumerable<ValueTuple<TKey, TValue>> kvps)
{
foreach (var kvp in kvps)
source.Add(kvp.Item1, kvp.Item2);
return source;
}
public static void AddTo<TKey, TValue>(this IEnumerable<ValueTuple<TKey, TValue>> source, Dictionary<TKey, TValue> target)
{
target.AddRange(source);
}
}
같이 사용
segments
.Zip(values, (s, v) => (s.AsSpan().StartsWith("{") ? s.Trim('{', '}') : null, v))
.Where(zip => zip.Item1 != null)
.AddTo(queryParams);
다른 사람들이 언급했듯이 Dictionary<TKey,TVal>.AddRange
구현되지 않은 이유 는 중복이있는 경우를 처리하고 싶은 다양한 방법이 있기 때문입니다. 이것은 또한 대한 경우 Collection
나 인터페이스 등 IDictionary<TKey,TVal>
, ICollection<T>
등
단지 List<T>
그것을 구현, 당신은주의 할 것이다 IList<T>
인터페이스는 같은 이유로하지 않습니다 다음 예상 행동을 상황에 따라 크게 달라질 수 있습니다 컬렉션에 값의 범위를 추가 할 때.
질문의 맥락은 중복에 대해 걱정하지 않는다는 것을 암시합니다.이 경우 Linq를 사용하는 간단한 oneliner 대안이 있습니다.
MethodThatReturnAnotherDic().ToList.ForEach(kvp => dic.Add(kvp.Key, kvp.Value));