Get key in dictionary with optional tuple parameter - c#

I have a dictionary that looks like this Dictionary<Tuple<int, int, bool>, string>
Let's say I have an entry like (1, 2, true) = "Word"
I want to be able to index the dictionary by only doing (1, null, true) and I want it to return the entry I mentioned before since the first and third values in the tuple are the same (ignoring the second since it's null)
Is there a way to do this? If not what is a suggestion for a new structure that would accomplish this? Thanks

I'd suggest you to create a struct to use instead of your Tuple, overriding GethashCode() (implement Equals too, to be consistent) :
public struct MyStruct
{
public int? FirstNumber { get; set; }
public int? SecondNumber { get; set; }
public bool TheBoolean { get; set; }
public override bool Equals(object obj)
{
return this.Equals((MyStruct)obj);
}
public bool Equals(MyStruct obj)
{
return (!obj.FirstNumber.HasValue || !this.FirstNumber.HasValue ||
obj.FirstNumber.Value == this.FirstNumber.Value)
&& (!obj.SecondNumber.HasValue || !this.SecondNumber.HasValue ||
obj.SecondNumber.Value == this.SecondNumber.Value)
&& obj.TheBoolean == this.TheBoolean;
}
public override int GetHashCode()
{
return (!this.FirstNumber.HasValue ? 0 : this.FirstNumber.GetHashCode())
^ (!this.SecondNumber.HasValue ? 0 : this.SecondNumber.GetHashCode())
^ this.TheBoolean.GetHashCode();
}
}
public static void Test()
{
var obj1 = new MyStruct
{
FirstNumber = 1,
SecondNumber = 2,
TheBoolean = true
};
var obj2 = new MyStruct
{
FirstNumber = 1,
SecondNumber = null,
TheBoolean = true
};
var dico = new Dictionary<MyStruct, string>
{
{ obj1, "Word" }
};
var result = dico[obj2];
Console.WriteLine(result);
}

The idea behind a Dictionary<TKey, TValue> is that you really need to have identical match between your TKey, TValuepairs. So, according to your structure, you could try something like this:
Dictionary<tuple<int, bool>, string>
where, in the dictionary creation step, you take in two ints, and combine them into a new, unitque value to add to your dictionary. Here is an example:
public static void AddToDictionary(
this Dictionary<Tuple<int, bool>, string> dictionary,
Tuple<int, int, bool> oldKey,
string value)
{
var key = new Tuple<int, bool>(oldKey.Item1 + oldKey.Item2, oldKey.Item3);
dictionary[key] = value;
}
Bear in mind that the key values must be UNIQUE. Otherwise, you will overwrite older entries.
Another structure that you can have is a Dictionary of Dictionaries. Since you are only interested in the first int, and since the second int might be null, you can have this:
Dictionary<int, Dictionary<Tuple<int?, bool>, string>>
This way, you can get something like this:
var keyTuple = new Tuple<int, int?, bool>(1, null, true);
myDictionary[keyTuple .Item1][new Tuple<int?, bool>(keyTuple.Item2, keyTuple.Item3)]
= "string value";
But, again, it starts to get verbose... a lot.

Related

Linq group by except column

I have a class with large amount of properties that I need to group by almost all columns.
class Sample {
public string S1 { get; set; }
public string S2 { get; set; }
public string S3 { get; set; }
public string S4 { get; set; }
// ... all the way to this:
public string S99 { get; set; }
public decimal? N1 { get; set; }
public decimal? N2 { get; set; }
public decimal? N3 { get; set; }
public decimal? N4 { get; set; }
// ... all the way to this:
public decimal? N99 { get; set; }
}
From time to time I need to group by all columns except one or two decimal columns and return some result based on this (namely object with all the fields, but with some decimal value as a sum or max).
Is there are any extension method that would allow me to do something like this:
sampleCollection.GroupByExcept(x => x.N2, x => x.N5).Select(....);
instead of specifying all columns in object?
You won't find anything builtin that handles such a case. You'd have to create one yourself. Depending on how robust you need this to be, you could take a number of approaches.
The main hurdle you'll come across is how you'll generate the key type. In an ideal situation, the new keys that are generated would have their own distinct type. But it would have to be dynamically generated.
Alternatively, you could use another type that could hold multiple distinct values and still could be suitably used as the key. Problem here is that it will still have to be dynamically generated, but you will be using existing types.
A different approach you could take that doesn't involve generating new types, would be to use the existing source type, but reset the excluded properties to their default values (or not set them at all). Then they would have no effect on the grouping. This assumes you can create instances of this type and modify its values.
public static class Extensions
{
public static IQueryable<IGrouping<TSource, TSource>> GroupByExcept<TSource, TXKey>(this IQueryable<TSource> source, Expression<Func<TSource, TXKey>> exceptKeySelector) =>
GroupByExcept(source, exceptKeySelector, s => s);
public static IQueryable<IGrouping<TSource, TElement>> GroupByExcept<TSource, TXKey, TElement>(this IQueryable<TSource> source, Expression<Func<TSource, TXKey>> exceptKeySelector, Expression<Func<TSource, TElement>> elementSelector)
{
return source.GroupBy(BuildKeySelector(), elementSelector);
Expression<Func<TSource, TSource>> BuildKeySelector()
{
var exclude = typeof(TXKey).GetProperties()
.Select(p => (p.PropertyType, p.Name))
.ToHashSet();
var itemExpr = Expression.Parameter(typeof(TSource));
var keyExpr = Expression.MemberInit(
Expression.New(typeof(TSource).GetConstructor(Type.EmptyTypes)),
from p in typeof(TSource).GetProperties()
where !exclude.Contains((p.PropertyType, p.Name))
select Expression.Bind(p, Expression.Property(itemExpr, p))
);
return Expression.Lambda<Func<TSource, TSource>>(keyExpr, itemExpr);
}
}
}
Then to use it you would do this:
sampleCollection.GroupByExcept(x => new { x.N2, x.N5 })...
But alas, this approach won't work under normal circumstances. You won't be able to create new instances of the type within a query (unless you're using Linq to Objects).
If you're using Roslyn, you could generate that type as needed, then use that object as your key. Though that'll mean you'll need to generate the type asynchronously. So you probably will want to separate this from your query all together and just generate the key selector.
public static async Task<Expression<Func<TSource, object>>> BuildExceptKeySelectorAsync<TSource, TXKey>(Expression<Func<TSource, TXKey>> exceptKeySelector)
{
var exclude = typeof(TXKey).GetProperties()
.Select(p => (p.PropertyType, p.Name))
.ToHashSet();
var properties =
(from p in typeof(TSource).GetProperties()
where !exclude.Contains((p.PropertyType, p.Name))
select p).ToList();
var targetType = await CreateTypeWithPropertiesAsync(
properties.Select(p => (p.PropertyType, p.Name))
);
var itemExpr = Expression.Parameter(typeof(TSource));
var keyExpr = Expression.New(
targetType.GetConstructors().Single(),
properties.Select(p => Expression.Property(itemExpr, p)),
targetType.GetProperties()
);
return Expression.Lambda<Func<TSource, object>>(keyExpr, itemExpr);
async Task<Type> CreateTypeWithPropertiesAsync(IEnumerable<(Type type, string name)> properties) =>
(await CSharpScript.EvaluateAsync<object>(
AnonymousObjectCreationExpression(
SeparatedList(
properties.Select(p =>
AnonymousObjectMemberDeclarator(
NameEquals(p.name),
DefaultExpression(ParseTypeName(p.type.FullName))
)
)
)
).ToFullString()
)).GetType();
}
To use this:
sampleCollection.GroupBy(
await BuildExceptKeySelector((CollectionType x) => new { x.N2, x.N5 })
).Select(....);
Borrowing from this answer here:
Create a class EqualityComparer
public class EqualityComparer<T> : IEqualityComparer<T>
{
public bool Equals(T x, T y)
{
IDictionary<string, object> xP = x as IDictionary<string, object>;
IDictionary<string, object> yP = y as IDictionary<string, object>;
if (xP.Count != yP.Count)
return false;
if (xP.Keys.Except(yP.Keys).Any())
return false;
if (yP.Keys.Except(xP.Keys).Any())
return false;
foreach (var pair in xP)
if (pair.Value.Equals( yP[pair.Key])==false)
return false;
return true;
}
public int GetHashCode(T obj)
{
return obj.ToString().GetHashCode();
}
}
Then create your GroupContent method:
private void GroupContent<T>(List<T> dataList, string[] columns, string[] columnsToExclude)
{
string[] columnsToGroup = columns.Except(columnsToExclude).ToArray();
EqualityComparer<IDictionary<string, object>> equalityComparer = new EqualityComparer<IDictionary<string, object>>();
var groupedList = dataList.GroupBy(x =>
{
var groupByColumns = new System.Dynamic.ExpandoObject();
((IDictionary<string, object>)groupByColumns).Clear();
foreach (string column in columnsToGroup)
((IDictionary<string, object>)groupByColumns).Add(column, GetPropertyValue(x, column));
return groupByColumns;
}, equalityComparer);
foreach (var item in groupedList)
{
Console.WriteLine("Group : " + string.Join(",", item.Key));
foreach (object obj in item)
Console.WriteLine("Item : " + obj);
Console.WriteLine();
}
}
private static object GetPropertyValue(object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName).GetValue(obj, null);
}
I extended the code above borrowing another answer.
public static class IEnumerableExt {
public static IEnumerable<T> GroupBye<T, C>(this IEnumerable<T> query, Func<IGrouping<IDictionary<string, object>, T>, C> grouping) where T : class
{
var cProps = typeof(C).GetProperties().Select(prop => prop.Name).ToArray();
var columnsToGroup = typeof(T).GetProperties().Select(prop => prop.Name).Except(cProps).ToArray();
var equalityComparer = new EqualityComparer<IDictionary<string, object>>();
return query
.GroupBy(x => ExpandoGroupBy(x, columnsToGroup), equalityComparer)
.Select(x => MergeIntoNew(x, grouping, cProps));
}
private static IDictionary<string, object> ExpandoGroupBy<T>(T x, string[] columnsToGroup) where T : class
{
var groupByColumns = new System.Dynamic.ExpandoObject() as IDictionary<string, object>;
groupByColumns.Clear();
foreach (string column in columnsToGroup)
groupByColumns.Add(column, typeof(T).GetProperty(column).GetValue(x, null));
return groupByColumns;
}
private static T MergeIntoNew<T, C>(IGrouping<IDictionary<string, object>, T> x, Func<IGrouping<IDictionary<string, object>, T>, C> grouping, string[] cProps) where T : class
{
var tCtor = typeof(T).GetConstructors().Single();
var tCtorParams = tCtor.GetParameters().Select(param => param.Name).ToArray();
//Calling grouping lambda function
var grouped = grouping(x);
var paramsValues = tCtorParams.Select(p => cProps.Contains(p) ? typeof(C).GetProperty(p).GetValue(grouped, null) : x.Key[p]).ToArray();
return (T)tCtor.Invoke(paramsValues);
}
private class EqualityComparer<T> : IEqualityComparer<T>
{
public bool Equals(T x, T y)
{
var xDict = x as IDictionary<string, object>;
var yDict = y as IDictionary<string, object>;
if (xDict.Count != yDict.Count)
return false;
if (xDict.Keys.Except(yDict.Keys).Any())
return false;
if (yDict.Keys.Except(xDict.Keys).Any())
return false;
foreach (var pair in xDict)
if (pair.Value == null && yDict[pair.Key] == null)
continue;
else if (pair.Value == null || !pair.Value.Equals(yDict[pair.Key]))
return false;
return true;
}
public int GetHashCode(T obj)
{
return obj.ToString().GetHashCode();
}
}
}
Which can be used in the following way:
var list = enumerable.GroupBye(grp => new
{
Value = grp.Sum(val => val.Value)
});
The result will like grouping all other columns but Value, which will be valued to the sum of grouped elements' value

Simple If and Else If Using Functions

Given this example snippet:
if(test == 5) {
var = 5;
var2 = 6;
}
else if(test == 6){
var = 30;
var2 = 25;
}
//...ect
How can I clean this up into a function? I thought of doing this:
void doStuff(int condition, int v1, int v2){
if(test == condition){
var = v1;
var2 = v2;
}
}
but then I would have to implement it like this:
doStuff(5,5,6);
doStuff(6,30,25);
//...ect
This would go through each function and check each if statement even if the first was evaluated to be true. This would not have the if, else if, else function unless I did something like this:
//Assuming doStuff returns a bool
if(doStuff(5,5,6)
else if(doStuff(6,30,25))
//...ect
Is there a better way to put functions inside conditional if / else if statements?
The switch approach is probably the best, but if the number of cases is huge, you can consider doing something funny like...
private static readonly Dictionary<int, int[]> _dic = new...
private void Foo()
{
var test = ...
var vals = this._dic[test];
a = vals[0];
b = vals[1];
...
}
And if not all variables are of type int, you can either use Tuple or your own structure to hold the information (+1 for naming values)
Edit: regarding your updated question about "better way to use methods in if-else":
doStuff(5,5,6) || doStuff(6,30,25) || ...
Will evaluate doStuff's from left to right until one returns true.
var conditionsMap = new Dictionary<int, Tuple<int, int>>();
conditionsMap.Add(5, new Tuple<int, int>(5, 6));
conditionsMap.Add(6, new Tuple<int, int>(30, 25));
foreach (var entry in conditionsMap)
{
var key = entry.Key;
var var1 = entry.Value.Item1;
var var2 = entry.Value.Item2;
Console.WriteLine("{0}\n{1}\n{3}", key, var1, var2);
}
In these situations, I tend to use an enum with custom attributes to define the different conditions. To me, that keeps it concise which aids in maintenance and also helps restrict the possible inputs to a predefined set.
Here's a full code sample (it can be pasted into LINQPad to test):
public class ValuesAttribute : Attribute
{
public int V1 { get; private set; }
public int V2 { get; private set; }
public ValuesAttribute(int v1, int v2)
{
V1 = v1;
V2 = v2;
}
}
public enum ValuesCase
{
[Values(5, 6)]
Five,
[Values(30, 25)]
Six
}
public void DoStuff(ValuesCase valuesCase)
{
// There are several ways to get an attribute value of an enum, but I like this one
ValuesAttribute values = valuesCase
.GetType()
.GetTypeInfo()
.GetDeclaredField(valuesCase.ToString())
.GetCustomAttribute<ValuesAttribute>();
if(values != null)
{
// Assign your variables or do whatever else you want here
Console.WriteLine(string.Join(", ", valuesCase.ToString(), values.V1, values.V2));
}
}
void Main()
{
DoStuff(ValuesCase.Five);
DoStuff(ValuesCase.Six);
}

Store reference to an object in dictionary

I have been searching for a way to save the references of variables of various types into a dictionary, together with a corresponding key. Then i would like to modify the instance of the variable by accessing its reference through the dictionary by its key.
For storing the references, i tried to use <object>, but without success. Neither Dictionaries nor Lists accept anything like Dictionary<string, ref int>.
The following code compiles, but seems to update the variables by value only. Any ideas or workarounds?
Here's the (tested) code:
class Test1
{
IDictionary<string, object> MyDict = new Dictionary<string, object>();
public void saveVar(string key, ref int v) //storing the ref to an int
{
MyDict.Add(key, v);
}
public void saveVar(string key, ref string s) //storing the ref to a string
{
MyDict.Add(key, s);
}
public void changeVar(string key) //changing any of them
{
if(MyDict.GetType() == typeof(int))
{
MyDict[key] = (int)MyDict[key] * 2;
}
if(MyDict.GetType() == typeof(string))
{
MyDict[key] = "Hello";
}
}
}
And this is how i call the methods of the class
Test1 t1 = new Test1();
int myInt = 3;
string myString = "defaultString";
Console.WriteLine(myInt); //returns "3"
Console.WriteLine(myString); //returns "defaultString"
t1.saveVar("key1", ref myInt);
t1.saveVar("key2", ref myString);
t1.changeVar("key1");
t1.changeVar("key2");
Console.WriteLine(myInt); //should return "6"
Console.WriteLine(myString); //should return "Hello"
The best solution I can think of for this is to store delegates in the dictionary that will allow you to retrieve and modify the variables.
Let’s start by declaring a type that contains a getter and a setter delegate:
sealed class VariableReference
{
public Func<object> Get { get; private set; }
public Action<object> Set { get; private set; }
public VariableReference(Func<object> getter, Action<object> setter)
{
Get = getter;
Set = setter;
}
}
The dictionary would have the type:
Dictionary<string, VariableReference>
To store a variable, say foo of type string, in the dictionary, you’d write the following:
myDic.Add(key, new VariableReference(
() => foo, // getter
val => { foo = (string) val; } // setter
));
To retrieve the value of a variable, you’d write
var value = myDic[key].Get();
To change the value of a variable to newValue, you’d write
myDic[key].Set(newValue);
This way, the variable that you’re changing is genuinely the original variable foo, and foo can be anything (a local variable, a parameter, a field on an object, a static field... even a property).
Putting this all together, this is what the class Test1 would look like:
class Test1
{
Dictionary<string, VariableReference> MyDict = new Dictionary<string, VariableReference>();
public void saveVar(string key, Func<object> getter, Action<object> setter)
{
MyDict.Add(key, new VariableReference(getter, setter));
}
public void changeVar(string key) // changing any of them
{
if (MyDict[key].Get() is int)
{
MyDict[key].Set((int)MyDict[key].Get() * 2);
}
else if (MyDict[key].Get() is string)
{
MyDict[key].Set("Hello");
}
}
}
// ...
Test1 t1 = new Test1();
int myInt = 3;
string myString = "defaultString";
Console.WriteLine(myInt); // prints "3"
Console.WriteLine(myString); // prints "defaultString"
t1.saveVar("key1", () => myInt, v => { myInt = (int) v; });
t1.saveVar("key2", () => myString, v => { myString = (string) v; });
t1.changeVar("key1");
t1.changeVar("key2");
Console.WriteLine(myInt); // actually prints "6"
Console.WriteLine(myString); // actually prints "Hello"
Apart from the problem Kevin points out, you need to wrap your value types in some kind of reference type.
The problem, as you've figured out, is that generic types don't work with the ref keyword, and when you assign a new value type into your dictionary, it's replacing the reference with a different reference, not updating it. There is no way to retain the ref semantics once you assign it to the dictionary.
But, what you could do is something like this, simply wrap the value type in a reference type:
public class MyRef<T> {
public T Ref {get;set;}
}
public class Test1
{
Dictionary<string, object> MyDict = new Dictionary<string, object>();
public void saveVar(string key, object v)
{
MyDict.Add(key, v);
}
public void changeVar(string key, object newValue) //changing any of them
{
var ref1 = MyDict[key] as MyRef<int>;
if (ref1 != null) {
ref1.Ref = (int)newValue;
return; // no sense in wasting cpu cycles
}
var ref2 = MyDict[key] as MyRef<string>;
if (ref2 != null) {
ref2.Ref = newValue.ToString();
}
}
public void DoIt()
{
var v = new MyRef<int> { Ref = 1 };
saveVar("First", v);
changeVar("First", 2);
Console.WriteLine(v.Ref.ToString()); // Should print 2
Console.WriteLine(((MyRef<int>)MyDict["First"]).Ref.ToString()); // should also print 2
}
}
A ref parameter's reference can not leave the scope of the method that calls it. This is because the variable that is reference cannot be guaranteed to be in scope after the method call has finished. You need to use a tool other than ref to create a layer of indirection allowing a variable a caller is using to be mutated.
Doing this is quite easy though. You simply need a class with a mutable member:
public class Pointer
{
public object Value { get; set; }
}
You can now write:
class Test1
{
IDictionary<string, Pointer> MyDict = new Dictionary<string, Pointer>();
public void saveVar(string key, Pointer pointer) //storing the ref to an int
{
MyDict.Add(key, pointer);
}
public void changeVar(string key) //changing any of them
{
if (MyDict[key].Value.GetType() == typeof(int))
{
MyDict[key].Value = (int)(MyDict[key].Value) * 2;
}
if (MyDict[key].Value.GetType() == typeof(string))
{
MyDict[key].Value = "Hello";
}
}
}
Since you're now mutating a reference type that the caller also has a reference to, they can observe the change to its value.

Compare two dictionaries for equality

I want to compare in C# two dictionaries with as keys a string and as value a list of ints. I assume two dictionaries to be equal when they both have the same keys and for each key as value a list with the same integers (both not necessarily in the same order).
I use both the answers from this and this related question, but both fail my test suite for the test functions DoesOrderKeysMatter and DoesOrderValuesMatter.
My test suite:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq;
namespace UnitTestProject1
{
[TestClass]
public class ProvideReportTests
{
[TestMethod]
public void AreSameDictionariesEqual()
{
// arrange
Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
List<int> list1 = new List<int>();
list1.Add(1);
list1.Add(2);
dict1.Add("a", list1);
List<int> list2 = new List<int>();
list2.Add(3);
list2.Add(4);
dict1.Add("b", list2);
// act
bool dictsAreEqual = false;
dictsAreEqual = AreDictionariesEqual(dict1, dict1);
// assert
Assert.IsTrue(dictsAreEqual, "Dictionaries are not equal");
}
[TestMethod]
public void AreDifferentDictionariesNotEqual()
{
// arrange
Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
List<int> list1 = new List<int>();
list1.Add(1);
list1.Add(2);
dict1.Add("a", list1);
List<int> list2 = new List<int>();
list2.Add(3);
list2.Add(4);
dict1.Add("b", list2);
Dictionary<string, List<int>> dict2 = new Dictionary<string, List<int>>();
// act
bool dictsAreEqual = true;
dictsAreEqual = AreDictionariesEqual(dict1, dict2);
// assert
Assert.IsFalse(dictsAreEqual, "Dictionaries are equal");
}
[TestMethod]
public void DoesOrderKeysMatter()
{
// arrange
Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
List<int> list1 = new List<int>();
list1.Add(1);
list1.Add(2);
dict1.Add("a", list1);
List<int> list2 = new List<int>();
list2.Add(3);
list2.Add(4);
dict1.Add("b", list2);
Dictionary<string, List<int>> dict2 = new Dictionary<string, List<int>>();
List<int> list3 = new List<int>();
list3.Add(3);
list3.Add(4);
dict2.Add("b", list3);
List<int> list4 = new List<int>();
list4.Add(1);
list4.Add(2);
dict2.Add("a", list4);
// act
bool dictsAreEqual = false;
dictsAreEqual = AreDictionariesEqual(dict1, dict2);
// assert
Assert.IsTrue(dictsAreEqual, "Dictionaries are not equal");
}
[TestMethod]
public void DoesOrderValuesMatter()
{
// arrange
Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
List<int> list1 = new List<int>();
list1.Add(1);
list1.Add(2);
dict1.Add("a", list1);
List<int> list2 = new List<int>();
list2.Add(3);
list2.Add(4);
dict1.Add("b", list2);
Dictionary<string, List<int>> dict2 = new Dictionary<string, List<int>>();
List<int> list3 = new List<int>();
list3.Add(2);
list3.Add(1);
dict2.Add("a", list3);
List<int> list4 = new List<int>();
list4.Add(4);
list4.Add(3);
dict2.Add("b", list4);
// act
bool dictsAreEqual = false;
dictsAreEqual = AreDictionariesEqual(dict1, dict2);
// assert
Assert.IsTrue(dictsAreEqual, "Dictionaries are not equal");
}
private bool AreDictionariesEqual(Dictionary<string, List<int>> dict1, Dictionary<string, List<int>> dict2)
{
return dict1.Keys.Count == dict2.Keys.Count &&
dict1.Keys.All(k => dict2.ContainsKey(k) && object.Equals(dict2[k], dict1[k]));
// also fails:
// return dict1.OrderBy(kvp => kvp.Key).SequenceEqual(dict2.OrderBy(kvp => kvp.Key));
}
}
}
What is the correct way to compare these kind of dictionaries? Or is there an error in my (admittedly clumsily written) TestSuite?
Update
I'm trying to incorporate Servy's answer in my test suite, like below, but I get some errors (underlined with a red wiggly line in Visual Studio):
SetEquals in the `Equals method says: "does not contain a definition for SetEquals accepting a first argument of type Generic.List.
In AreDictionariesEqualit saysDictionaryComparer<List> is a type but is used as a variable.`
namespace UnitTestProject1
{
[TestClass]
public class ProvideReportTests
{
[TestMethod]
// ... same as above
private bool AreDictionariesEqual(Dictionary<string, List<int>> dict1, Dictionary<string, List<int>> dict2)
{
DictionaryComparer<string, List<int>>(new ListComparer<int>() dc = new DictionaryComparer<string, List<int>>(new ListComparer<int>();
return dc.Equals(dict1, dict2);
}
}
public class DictionaryComparer<TKey, TValue> :
IEqualityComparer<Dictionary<TKey, TValue>>
{
private IEqualityComparer<TValue> valueComparer;
public DictionaryComparer(IEqualityComparer<TValue> valueComparer = null)
{
this.valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
}
public bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y)
{
if (x.Count != y.Count)
return false;
if (x.Keys.Except(y.Keys).Any())
return false;
if (y.Keys.Except(x.Keys).Any())
return false;
foreach (var pair in x)
if (!valueComparer.Equals(pair.Value, y[pair.Key]))
return false;
return true;
}
public int GetHashCode(Dictionary<TKey, TValue> obj)
{
throw new NotImplementedException();
}
}
public class ListComparer<T> : IEqualityComparer<List<T>>
{
private IEqualityComparer<T> valueComparer;
public ListComparer(IEqualityComparer<T> valueComparer = null)
{
this.valueComparer = valueComparer ?? EqualityComparer<T>.Default;
}
public bool Equals(List<T> x, List<T> y)
{
return x.SetEquals(y, valueComparer);
}
public int GetHashCode(List<T> obj)
{
throw new NotImplementedException();
}
}
public static bool SetEquals<T>(this IEnumerable<T> first, IEnumerable<T> second, IEqualityComparer<T> comparer)
{
return new HashSet<T>(second, comparer ?? EqualityComparer<T>.Default)
.SetEquals(first);
}
}
So first we need an equality comparer for dictionaries. It needs to ensure that they have matching keys and, if they do, compare the values of each key:
public class DictionaryComparer<TKey, TValue> :
IEqualityComparer<Dictionary<TKey, TValue>>
{
private IEqualityComparer<TValue> valueComparer;
public DictionaryComparer(IEqualityComparer<TValue> valueComparer = null)
{
this.valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
}
public bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y)
{
if (x.Count != y.Count)
return false;
if (x.Keys.Except(y.Keys).Any())
return false;
if (y.Keys.Except(x.Keys).Any())
return false;
foreach (var pair in x)
if (!valueComparer.Equals(pair.Value, y[pair.Key]))
return false;
return true;
}
public int GetHashCode(Dictionary<TKey, TValue> obj)
{
throw new NotImplementedException();
}
}
but this isn't enough on its own. We need to compare the values of the dictionary using another custom comparer, not the default comparer as the default list comparer won't look at the values of the list:
public class ListComparer<T> : IEqualityComparer<List<T>>
{
private IEqualityComparer<T> valueComparer;
public ListComparer(IEqualityComparer<T> valueComparer = null)
{
this.valueComparer = valueComparer ?? EqualityComparer<T>.Default;
}
public bool Equals(List<T> x, List<T> y)
{
return x.SetEquals(y, valueComparer);
}
public int GetHashCode(List<T> obj)
{
throw new NotImplementedException();
}
}
Which uses the following extension method:
public static bool SetEquals<T>(this IEnumerable<T> first, IEnumerable<T> second,
IEqualityComparer<T> comparer)
{
return new HashSet<T>(second, comparer ?? EqualityComparer<T>.Default)
.SetEquals(first);
}
Now we can simply write:
new DictionaryComparer<string, List<int>>(new ListComparer<int>())
.Equals(dict1, dict2);
I know this question already has an accepted answer, but I'd like to offer an even simpler alternative:
using System.Linq;
using System.Collections.Generic;
namespace Foo
{
public static class DictionaryExtensionMethods
{
public static bool ContentEquals<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, Dictionary<TKey, TValue> otherDictionary)
{
return (otherDictionary ?? new Dictionary<TKey, TValue>())
.OrderBy(kvp => kvp.Key)
.SequenceEqual((dictionary ?? new Dictionary<TKey, TValue>())
.OrderBy(kvp => kvp.Key));
}
}
}
Convert the dictionary to a KeyValuePair list and then compare as collections:
CollectionAssert.AreEqual(
dict1.OrderBy(kv => kv.Key).ToList(),
dict2.OrderBy(kv => kv.Key).ToList()
);
I think that AreDictionariesEqual() just needs another method for List comparison
So if order of entries doesn't matter you can try this:
static bool ListEquals(List<int> L1, List<int> L2)
{
if (L1.Count != L2.Count)
return false;
return L1.Except(L2).Count() == 0;
}
/*
if it is ok to change List content you may try
L1.Sort();
L2.Sort();
return L1.SequenceEqual(L2);
*/
static bool DictEquals(Dictionary<string, List<int>> D1, Dictionary<string, List<int>> D2)
{
if (D1.Count != D2.Count)
return false;
return D1.Keys.All(k => D2.ContainsKey(k) && ListEquals(D1[k],D2[k]));
}
And if order of entries matters, try this:
static bool DictEqualsOrderM(Dictionary<string, List<int>> D1, Dictionary<string, List<int>> D2)
{
if (D1.Count != D2.Count)
return false;
//check keys for equality, than lists.
return (D1.Keys.SequenceEqual(D2.Keys) && D1.Keys.All(k => D1[k].SequenceEqual(D2[k])));
}
The accepted answer above will not always return a correct comparison because
using a HashSet to compare 2 lists will not account for duplicate values in the lists.
For instance if the OP had:
var dict1 = new Dictionary<string, List<int>>() { { "A", new List<int>() { 1, 2, 1 } } };
var dict2 = new Dictionary<string, List<int>>() { { "A", new List<int>() { 2, 2, 1 } } };
Then the result of the dictionary comparison is they are equal, when they are not. The only solution I see is to sort the 2 list and compare the values by index, but I'm sure someone smarter then me can come up with a more efficient way.
Here is a way using Linq, probably sacrificing some efficiency for tidy code. The other Linq example from jfren484 actually fails the DoesOrderValuesMatter() test, because it depends on the default Equals() for List<int>, which is order-dependent.
private bool AreDictionariesEqual(Dictionary<string, List<int>> dict1, Dictionary<string, List<int>> dict2)
{
string dict1string = String.Join(",", dict1.OrderBy(kv => kv.Key).Select(kv => kv.Key + ":" + String.Join("|", kv.Value.OrderBy(v => v))));
string dict2string = String.Join(",", dict2.OrderBy(kv => kv.Key).Select(kv => kv.Key + ":" + String.Join("|", kv.Value.OrderBy(v => v))));
return dict1string.Equals(dict2string);
}
If two dictionaries are known to use equivalent implementations of IEqualityComparer, and one wishes to regard as equivalent all keys which that implementation regardss as equivalent, they contain the same number of items, and one (arbitrarily chosen) maps all of the elements keys found in the other to corresponding values from the other, they will be equivalent unless or until one of them is modified. Testing for those conditions will be faster than any approach which does not not assume that both dictionaries use the same IEqualityComparer.
If two dictionaries do not use the same implementation of IEqualityComparer, they should generally not be considered equivalent regardless of the items they contain. For example, a Dictionary<String,String> with a case-sensitive comparer and one with a case-insensitive comparer, both of which contain the key-value pair ("Fred", "Quimby") are not equivalent, since the latter would map "FRED" to "Quimby", but the former would not.
Only if the dictionaries use the same implementation of IEqualityComparer, but if one is interested in a finer-grained definition of key-equality than the one used by the dictionaries and a copy of the key is not stored with each value, it will it be necessary to build a new dictionary for the purpose of testing the original dictionaries for equality. It may be best to delay this step until the earlier test has suggested that the dictionaries seem to match. Then build a Dictionary<TKey,TKey> which maps each key from one of the dictionaries to itself, and then look up all of the other dictionary's keys in that to make sure that they map to things which match. If both dictionaries used case-insensitive comparers, and one contained ("Fred", "Quimby") and the other ("FRED", "Quimby"), the new temporary dictionary would map "FRED" to "Fred", and comparing those two strings would reveal that the dictionaries don't match.
Most of the answers are iterating the dictionaries multiple times while it should be simple:
static bool AreEqual(IDictionary<string, string> thisItems, IDictionary<string, string> otherItems)
{
if (thisItems.Count != otherItems.Count)
{
return false;
}
var thisKeys = thisItems.Keys;
foreach (var key in thisKeys)
{
if (!(otherItems.TryGetValue(key, out var value) &&
string.Equals(thisItems[key], value, StringComparison.OrdinalIgnoreCase)))
{
return false;
}
}
return true;
}
I like this approach because it gives more details when the test fails
public void AssertSameDictionary<TKey,TValue>(Dictionary<TKey,TValue> expected,Dictionary<TKey,TValue> actual)
{
string d1 = "expected";
string d2 = "actual";
Dictionary<TKey,TValue>.KeyCollection keys1= expected.Keys;
Dictionary<TKey,TValue>.KeyCollection keys2= actual.Keys;
if (actual.Keys.Count > expected.Keys.Count)
{
string tmp = d1;
d1 = d2;
d2 = tmp;
Dictionary<TKey, TValue>.KeyCollection tmpkeys = keys1;
keys1 = keys2;
keys2 = tmpkeys;
}
foreach(TKey key in keys1)
{
Assert.IsTrue(keys2.Contains(key), $"key '{key}' of {d1} dict was not found in {d2}");
}
foreach (TKey key in expected.Keys)
{
//already ensured they both have the same keys
Assert.AreEqual(expected[key], actual[key], $"for key '{key}'");
}
}
public static IDictionary<string, object> ToDictionary(this object source)
{
var fields = source.GetType().GetFields(
BindingFlags.GetField |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source) ?? string.Empty
);
var properties = source.GetType().GetProperties(
BindingFlags.GetField |
BindingFlags.GetProperty |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source, null) ?? string.Empty
);
return fields.Concat(properties).ToDictionary(key => key.Key, value => value.Value); ;
}
public static bool EqualsByValue(this object source, object destination)
{
var firstDic = source.ToFlattenDictionary();
var secondDic = destination.ToFlattenDictionary();
if (firstDic.Count != secondDic.Count)
return false;
if (firstDic.Keys.Except(secondDic.Keys).Any())
return false;
if (secondDic.Keys.Except(firstDic.Keys).Any())
return false;
return firstDic.All(pair =>
pair.Value.ToString().Equals(secondDic[pair.Key].ToString())
);
}
public static bool IsAnonymousType(this object instance)
{
if (instance == null)
return false;
return instance.GetType().Namespace == null;
}
public static IDictionary<string, object> ToFlattenDictionary(this object source, string parentPropertyKey = null, IDictionary<string, object> parentPropertyValue = null)
{
var propsDic = parentPropertyValue ?? new Dictionary<string, object>();
foreach (var item in source.ToDictionary())
{
var key = string.IsNullOrEmpty(parentPropertyKey) ? item.Key : $"{parentPropertyKey}.{item.Key}";
if (item.Value.IsAnonymousType())
return item.Value.ToFlattenDictionary(key, propsDic);
else
propsDic.Add(key, item.Value);
}
return propsDic;
}
Comparing dictionary using string keys is way more complex than what it looks at first glance.
Dictionary<TKey,TValue> uses an IEqualityComparer<TKey> every time you access an entry in the dictionary to compare your input with the actual entries. The comparer is also used for hash calculations, which serves as some kind of index for faster random access to the entries. Trying to compare dictionaries with different comparers may have some side effects on key sorting and equality considerations for the key-value pair. The key point here is you need to compare the comparers too when comparing dictionaries.
Dictionary<TKey,TValue> also provides collections of keys and values, but they are unsorted. The keys and values collections are consistent inside the dictionary (the nth key is the nth value's key), but not across instances. This means we'll have to work with KeyValuePairs<TKey,TValue> and sort them by key on both dictionaries before comparing them.
However, the comparers in the dictionary only check for equality, it's not able to sort the keys. In order to sort pairs, we'll need a new IComparer<TKey> instance, which is another interface than IEqualityComparer<TKey>. But there's a trap here: default implementations of these two interfaces are not consistent. When you create a dictionary using the default contructor, the class will instanciate a GenericEqualityComparer<TKey> if TKey implements IEquatable<TKey>, which require TKey to implement bool Equals(TKey other); (otherwise, it will fallback to an ObjectEqualityComparer). If you create default Comparer, that will instanciate a GenericComparer<TKey> if TKey implements IComparable<TKey>, which will require TKey to implement int CompareTo(TKey other); (otherwise it will default to an ObjectComparer). Not all types implement both interfaces, and those who do sometimes use different implementations. There is a risk that two different keys (according to Equals) are sorted identically (according to CompareTo). In that case, there's a risk on the key sorting consitency.
Fortunately, string implements both interfaces. Unfortunately, its implementations are NOT consistent: CompareTo depends on the current culture to sort items, whereas Equals does not ! The solution to this problem is to inject a custom comparer to the dictionary, which provides consistent implementation of both interface. We can use StringComparer for that rather than relying on the default implementation. Then we'll simply get the dictionary comparer, cast it, and use it for sorting keys. Also, StringComparer allows comparing the comparers, so we can ensure both dictionaries use the same one.
First, we need a way to compare the values of the dictionary. Since you want to compare lists of int without order, we'll implement an generic equality comparer that sorts items and SequenceEqual them.
internal class OrderInsensitiveListComparer<TValue>
: IEqualityComparer<IEnumerable<TValue>>
{
private readonly IComparer<TValue> comparer;
public OrderInsensitiveListComparer(IComparer<TValue> comparer = null)
{
this.comparer = comparer ?? Comparer<TValue>.Default;
}
public bool Equals([AllowNull] IEnumerable<TValue> x, [AllowNull] IEnumerable<TValue> y)
{
return x != null
&& y != null
&& Enumerable.SequenceEqual(
x.OrderBy(value => value, comparer),
y.OrderBy(value => value, comparer));
}
public int GetHashCode([DisallowNull] IEnumerable<TValue> obj)
{
return obj.Aggregate(17, (hash, item) => hash * 23 ^ item.GetHashCode());
}
}
Now, we've got the values covered, but we also need to compare KeyValuePair. It is a simple ref struct, so we don't need to check for nulls. We'll simply delegate the comparison to two comparers : one for the key, another for the value.
internal class KeyValuePairComparer<TKey, TValue> : IEqualityComparer<KeyValuePair<TKey, TValue>>
{
private readonly IEqualityComparer<TKey> key;
private readonly IEqualityComparer<TValue> value;
public KeyValuePairComparer(
IEqualityComparer<TKey> key = null,
IEqualityComparer<TValue> value = null)
{
this.key = key ?? EqualityComparer<TKey>.Default;
this.value = value ?? EqualityComparer<TValue>.Default;
}
public bool Equals([AllowNull] KeyValuePair<TKey, TValue> x, [AllowNull] KeyValuePair<TKey, TValue> y)
{
// KeyValuePair is a struct, you can't null check
return key.Equals(x.Key, y.Key) && value.Equals(x.Value, y.Value);
}
public int GetHashCode([DisallowNull] KeyValuePair<TKey, TValue> obj)
{
return 17 * 23 ^ obj.Key.GetHashCode() * 23 ^ obj.Value.GetHashCode();
}
}
Now, we can implement the dictionary comparer. We do null check and compare the dictionaries comparers. Then we consider the dictionary as a simple enumerable of KeyValuePair and SequenceEqual them after sorting them by key. For that, we cast the dictionary comparer and delegate the comparison to the KeyValueComparer.
internal class DictionaryComparer<TValue> : IEqualityComparer<Dictionary<string, TValue>>
{
private readonly IEqualityComparer<TValue> comparer;
public DictionaryComparer(
IEqualityComparer<TValue> comparer = null)
{
this.comparer = comparer ?? EqualityComparer<TValue>.Default;
}
public bool Equals([AllowNull] Dictionary<string, TValue> x, [AllowNull] Dictionary<string, TValue> y)
{
return x != null
&& y != null
&& Equals(x.Comparer, y.Comparer)
&& x.Comparer is StringComparer sorter
&& Enumerable.SequenceEqual(
x.AsEnumerable().OrderBy(pair => pair.Key, sorter),
y.AsEnumerable().OrderBy(pair => pair.Key, sorter),
new KeyValuePairComparer<string, TValue>(x.Comparer, comparer));
}
public int GetHashCode([DisallowNull] Dictionary<string, TValue> obj)
{
return new OrderInsensitiveListComparer<KeyValuePair<string, TValue>>()
.GetHashCode(obj.AsEnumerable()) * 23 ^ obj.Comparer.GetHashCode();
}
}
Finally, we only need to instanciate comparers and let them do the work.
private bool AreDictionariesEqual(Dictionary<string, List<int>> dict1, Dictionary<string, List<int>> dict2)
{
return new DictionaryComparer<List<int>>(
new OrderInsensitiveListComparer<int>())
.Equals(dict1, dict2);
}
However, for this to work, we need to use a StringComparer in every dictionary.
[TestMethod]
public void DoesOrderValuesMatter()
{
Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>(StringComparer.CurrentCulture);
// more stuff
}
The function shown below can be accommodated to perform any generic comparison:
public bool AreDictionaryEquals(Dictionary<ulong?, string> dictionaryList1, Dictionary<ulong?, string> dictionaryList2)
{
if (dictionaryList1.Count != dictionaryList2.Count)
return false;
IDictionary<ulong?, string> orderedList1 = new Dictionary<ulong?, string>();
IDictionary<ulong?, string> orderedList2 = new Dictionary<ulong?, string>();
foreach (var itemDict1 in dictionaryList1.OrderByDescending(key => key.Id))
{
orderedList1.Add(itemDict1.Id, itemDict1.PropertyX);
}
foreach (var itemDict2 in dictionaryList2.OrderByDescending(key => key.Id))
{
orderedList2.Add(itemDict2.Id, itemDict2.PropertyX);
}
//check keys and values for equality
return (orderedList1.Keys.SequenceEqual(orderedList2.Keys) && orderedList1.Keys.All(k => orderedList1[k].SequenceEqual(orderedList2[k])));
}
1- If the length of both dictionaries is not equal we can safely return false.
2- Then, we proceed to sort both dictionaries using the value of the keys. The reason for doing this is you could have situations like this:
Dictionary A: [1,A], [3,B]
Dictionary B: [3,B], [1,A]
Even though the order is not the same, the content of both can be considered equal.
Finally:
3- We compare both sorted sequences and retrieve the result of this comparison.

How to compare two lists that contain objects that contain Dictionaries?

I have two Lists (List “A” and List “B”) that hold objects of type “KeyStore”, which is shown below:
public class KeyStore
{
public Dictionary<string, string> PrimaryKeys { get; set; }
public KeyStore(string pkName, string pkValue)
{
PrimaryKeys = new Dictionary<string, string> {{pkName, pkValue}};
}
public KeyStore()
{
PrimaryKeys = new Dictionary<string, string>();
}
}
I need to look at each record in List “A” and see if there is a matching record in List “B”. If there is, then this record needs to be stored in a new list that contains just matching records. A match is considered true if a record’s PrimaryKeys dictionary contains the same number of entries and the same key value combination as a record in List “B”. The order of the entries in the dictionary is not important in testing for equality. If there is a record in List “A” that does not have a match in List “B”, then this needs to be stored in a new list that will only contain records found in List “A”.
Previously I did something similar when I had Lists of strings where I used “Intersect” and “Except” to create lists of matched and non-matched records. I’m assuming that now that I need to compare these KeyStore objects I need to go up a level of complexity. Can anyone offer a solution or advise on how I should approach this problem?
EDIT 1 ----------------
Based on comments, I have created a class that implements IEqualityComparer, as shown below:
class KeyStoreComparer : IEqualityComparer<KeyStore>
{
public bool Equals(KeyStore x, KeyStore y)
{
if (x != null && x.PrimaryKeys.Count == y.PrimaryKeys.Count)
{
return x.PrimaryKeys.Keys.All(k => y.PrimaryKeys.ContainsKey(k)) &&
x.PrimaryKeys.Keys.All(k => x.PrimaryKeys[k].Equals(y.PrimaryKeys[k]));
}
return false;
}
public int GetHashCode(KeyStore obj)
{
return ReferenceEquals(obj, null) ? 0 : obj.GetHashCode();
}
}
I have created some dummy data but when the "Intersect" command is run the above code is never called. Any ideas where I am going wrong?
var ListA = new List<KeyStore>();
ListA.Add(new KeyStore("a", "b"));
ListA.Add(new KeyStore("c", "d"));
var ListB = new List<KeyStore>();
ListB.Add(new KeyStore("a", "b"));
ListB.Add(new KeyStore("x", "y"));
var g = ListA.Intersect(ListB, new KeyStoreComparer());
The code in the "Equals" and "GetHashCode" may not be correct but I'm just trying to get it to get as far as running it before I can improve it.
EDIT 2 ---------------------------------------
I have made various changes to the KeyStore class as shown in the example by “fox” on this page. I still don’t get the overridden functions to be called. As an experiment I tried this:
var result = ListA.Equals(ListB);
When I do this the overridden functions in the KeyStor class don’t run. But if I do this:
var result = ListA[0].Equals(ListB[0]);
The overridden functions do run and give the expected result. Anyone know how I can get this to work for all items in the lists rather than just for individual records?
EDIT 3 ---------------------------------------
The problem I am seeing is that the override works fine for single items, eg:
var a = new KeyStore("a", "b");
var b = new KeyStore("a", "b");
var c = a.Equals(b);
When I run the above my break point on the KeyStore "Equals" function is hit. As soon as I try to do something similar but with a List of KeyStore, the breakpoint is no longer hit. Do I need to do something extra when working with Lists?
public class KeyStore
{
public Dictionary<string, string> PrimaryKeys { get; set; }
public KeyStore(string pkName, string pkValue)
{
PrimaryKeys = new Dictionary<string, string> { { pkName, pkValue } };
}
public KeyStore()
{
PrimaryKeys = new Dictionary<string, string>();
}
public override bool Equals(object obj)
{
// If parameter is null return false.
if (obj == null)
return false;
// If parameter cannot be cast to KeyStore return false.
KeyStore targetKeyStore = obj as KeyStore;
if (targetKeyStore == null)
return false;
return PrimaryKeys.OrderBy(pk => pk.Key).SequenceEqual(targetKeyStore.PrimaryKeys.OrderBy(pk => pk.Key));
}
public override int GetHashCode()
{
StringBuilder content = new StringBuilder();
foreach (var item in PrimaryKeys.OrderBy(pk => pk.Key))
content.AppendFormat("{0}-{1}", item.Key, item.Value);
return content.ToString().GetHashCode();
}
}
Eric Lippert's guide on implementing GetHashCode wrote that "equal items have equal hashes". GetHashCode() implementation above just to show concept, might not suitable for production code.
Overriding the ToString method will help simplify your code quite a bit. See if this helps:
public class KeyStore
{
public SortedDictionary<string, string> PrimaryKeys
{
get;
set;
}
public KeyStore(string pkName, string pkValue)
{
PrimaryKeys = new SortedDictionary<string, string> { { pkName, pkValue } };
}
public KeyStore()
{
PrimaryKeys = new SortedDictionary<string, string>();
}
public override bool Equals(object obj)
{
if(obj == null || (KeyStore)obj == null)
return false;
KeyStore temp = (KeyStore)obj;
return ToString() == temp.ToString();
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
public override string ToString()
{
return PrimaryKeys.Count.ToString() + " : \n" + string.Join("\n",(from kvp in PrimaryKeys
let s = kvp.Key + " - " + kvp.Value
select s));
}
}
List<KeyStore> Lista = new List<KeyStore>
{
new KeyStore("testa","testa1"),
new KeyStore("testb","testb1"),
new KeyStore("testc", "testc1")
};
List<KeyStore> Listb = new List<KeyStore>
{
new KeyStore("testa","testa1"),
new KeyStore("testd","testb1"),
new KeyStore("testc", "testa1"),
new KeyStore("teste", "teste1")
};
var Listc = Lista.Intersect(Listb).ToList();
var Listd = Lista.Except(Listb).ToList();
?Listc
Count = 1
[0]: {1 :
testa - testa1}
?Listd
Count = 2
[0]: {1 :
testb - testb1}
[1]: {1 :
testc - testc1}

Categories