제목은 충분히 기본적입니다. 내가 할 수없는 이유 :
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));