Store non-unique pairs in .net 2.0? - c#

I have to store a pair of strings that are non-unique, so I could have something like:
"A", "B"
"A","C"
I can't use a Dictionary or HashTable because I don't have a unique key. I thought about a couple options:
List<List<string>,List<string>> = new List<List<string>,List<string>>(); //Too long?
Create a class or struct (what is better?) to hold my pairs and then store that in list, so
class pairs
{
}
List<pairs> values = new List<pairs>();
I also just need to be able to enumerate through the pairs, so I don't need care about querying, sorting, comparing. Because of this, is it best to use the least specific type, such as Enumerable?

You should be able to use a List<KeyValuePair<string, string>>.
KeyValuePair<TKey, TValue> structs by themselves don't require unique keys, even if you collect them in a single list.
If you think KVPs don't ring well semantically with what you want, just go with a custom type. It shouldn't matter whether your custom type is a class or struct.

What about just a List<string[]>?
var list = new List<string[]>();
list.Add(new [] { "A", "B"});
foreach(var pair in list)
{
Console.WriteLine(pair[0] + pair[1]);
}

You could use a List<KeyValuePair<string,string>>, but to avoid the semantics of a key (since you said it wasn't unique), I would use a List<Tuple<string,string>> which is practically the same but with different semantics.

Related

Where to store two values for further comparing?

After click over row in DataGridView I take value of two cells in this row:
string id = Convert.ToString(dataGridView1.Rows[e.RowIndex].Cells["Number"].Value);
string type = Convert.ToString(dataGridView1.Rows[e.RowIndex].Cells["dataGridViewTextBoxColumn46"].Value);
So, in which structure to store these values, that to take off them further?
In result I need to compare if id, type are exist in structure.
I tried Dictionary<int, int>. But it is hard to check if there values are in Dictionary like this: Dictionary<'id', 'type'>
A simple HashSet<Tuple<string, string>> would probably do:
HashSet<T> is a set of values which provides O(1) average lookup time for "contains" queries.
Tuple<T1, T2> is a class representing a pair of values, which uses value-type equality semantics, i.e. implements Equals and GetHashCode using the values stored inside the class, meaning that two different instances with same members will be considered equal (and this is important if you want to use them as HashSet<T> keys.
So, you would simply do something like:
// somewhere in your method or class
HashSet<Tuple<string, string>> hashset = new HashSet<Tuple<string, string>>();
// once you get the (id, type) pair:
hashset.Add(Tuple.Create(id, key));
// to check if the items are in the hashset:
if (hashset.Contains(Tuple.Create("a", "b"))
{
// do stuff
}
// to remove the item from the hashset
hashset.Remove(Tuple.Create("a", "b"));

Multi-Keys Hash or Dictionary, a list of values as output

I am new to C# and need to have a general list with multiple keys.
I have three parameter which creates the key of my data. For each record (each three key) I have a set of values.
I need to have a generic list which the value of each node in the list be the values of my keys and the value of each node pointing to a list which contained the related values to that key.
Following is the example of my data and data structure which I am looking for:
Key1 Key2 Key3 Value1 Value2 Value3
0 0 0 a b c
0 0 1 d e f
0 1 1 g h -
<0,0,0, List(a,b,c)> ---> <0,0,1,list(d,e,f)>---> <0,1,1,List(g,h)>--->Null
I was thinking of having a hash table with multiple keys and the value which is pointing to an object that is a link list.
Or creating a dictionary with these three keys and again returning a pointer to the head of link list.
I would be appreciate if someone can tell me how can I do it in C#.
First, you definitely should use Dictionary<TKey, TValue>, and not HashTable. The non-generic collection types are really just there for backward compatibility. It is better to use the generic types for new code.
As for your specific problem, you'll note that the .NET dictionary types allow only a single key. Indeed, this is typical for dictionary collections generally. Each entry in the collection is a single key/value pair.
However, you can combine three key values into a single object value and use that as a key instead. And in fact, .NET provides various Tuple classes to accomplish just that, a different class for each count of type parameters, and thus for each count of items in the object. Furthermore, these classes all implement appropriate comparisions and hashing for use as a dictionary key.
Now, applying this to your question, you have some options, depending on what you really want to do. Unfortunately, it's not really clear what you want to do. :(
If you will only ever have a maximum of three values for each triplet of key values, then I think the suggestion from commenter Mephy is fine. You could declare your collection and initialize it something like this:
Dictionary<Tuple<int, int, int>, Tuple<string, string, string>> collection =
new Dictionary<Tuple<int, int, int>, Tuple<string, string, string>>
{
{ Tuple.Create(0, 0, 0), Tuple.Create("a", "b", "c") },
{ Tuple.Create(0, 0, 1), Tuple.Create("d", "e", "f") },
{ Tuple.Create(0, 1, 1), Tuple.Create("g", "h", null) },
};
Note that null is used to indicate a missing value in the dictionary's value tuple.
However, if you literally want a list object as the value, you could instead do something like this:
Dictionary<Tuple<int, int, int>, List<string>> collection =
new Dictionary<Tuple<int, int, int>, List<string>>
{
{ Tuple.Create(0, 0, 0), new List<string> { "a", "b", "c"} },
{ Tuple.Create(0, 0, 0), new List<string> { "d", "e", "f"} },
{ Tuple.Create(0, 0, 0), new List<string> { "g", "h" } },
};
As far as treating the above as a list of key/value pairs, like any .NET collection type, Dictionary<TKey, TValue> can be treated as an enumeration of values, in this case via implementation of IEnumerable<KeyValuePair<TKey, TValue>> where TKey and TValue are the same types used for the dictionary object itself. So you can for example do something like this:
foreach (KeyValuePair<Tuple<int, int, int>, List<string>> kvp in collection)
{
// here, kvp.Key will have the Tuple<int, int, int> key value
// for the dictionary entry, while kvp.Value will have the
// List<string> value for the same entry.
}
Note that the order of the enumeration of a dictionary type in .NET is undefined. You are not given any assurances elements will be returned in any particular order, e.g. in the order in which they were added. If you need a particular order, you'll have to impose that yourself somehow.
Finally, note in the above example the KeyValuePair<TKey, TValue> type. This is in fact just a special case of a tuple (though it pre-dates the actual Tuple... classes in .NET). I.e. it's a custom class designed specifically for the purpose of storing key/value pairs.
You can, if you want, declare such a type yourself to act as the key for your dictionary. This would have the advantage of allowing you to provide a specific, readable name for the type, and of course allowing you to avoid some of the verbosity that is involved in dealing with the Tuple... classes (the Tuple.Create() generic methods help, but declarations can still get unwieldy). Doing so comes of course at the expense of writing your own comparison and hash code.
You can find a middle ground, either by creating a class that inherits the Tuple... class you need, in which you implement just the constructor (passing the initialization parameters to the base constructor), e.g.:
class CustomKey : Tuple<int, int, int>
{
public CustomKey(int i1, int i2, int i3) : base(i1, i2, i3) { }
}
or by simply aliasing the Tuple... type in your module with a using directive, giving the Tuple... type a locally usable name that is more readable, e.g.:
using CustomKey = System.Tuple<int, int, int>;
The former gives you easy access to the readable name anywhere in your project but does require implementing a (very short) class; the latter requires less work, but will apply only in a single source file.

Using a foreach loop on a List of two types?

I know I can use a foreach loop as follows:
List<int> handles = GetHandles();
foreach (int handle in handles)
{
// do stuff
}
Can I do the same for a SortedList as follows?
SortedList<string, int> namesAndHandles;
EDIT: Sorry, made a typo. It should be a SortedList. Essentially I want to convert this to an IDictionary and access the handles based off a name
There's no such thing as List<string, int> - there's no List<,> type with two type parameters. If you've got a collection of name/handle pairs, you should either use List<Tuple<string, int>> or create your own NameAndHandle class. Either will work fine with foreach.
(You could create your own List<TFirst, TSecond> class if you really wanted, but I'd really advise against it.)
I bet List<KeyValuePair<string,int>> would do what you are looking for. You could iterate the collection and the .Key property holds the string and .Value holds the int

Sorting an ArrayList on a child's value in C#

I have an ArrayList that contains multiple String[] (String arrays).
Each String[] contains a key: name
What I want to do is sort the ArrayList based on the value of name.
Using Array.Sort did not work because it cannot compare String[]. How can I sort the ArrayList based on the value of name that is within the String[]?
In LINQ:
var ordered = arrayList.Cast<String[]>().OrderBy(ar => arr[0]);
Normally it's better to use a strong typed List<String[]> instead of an ArrayList. When you say key it sounds as if you want to have a list of strings for each unique key. Then a Dictionary<String, String[]> would be the best choice.
You should be using a List<string[]>. It supports everything that ArrayList does, and it is strongly typed.
Using a List<string[]>:
IEnumerable<string[]> sorted = myList.OrderBy(s => s[0]);
Try this
ArrayList arr = new ArrayList();
arr.ToArray().OrderBy(p => p);
Use Array.Sort(Array,IComparer) and write your own Comparer that looks for your key in your String[] and compare them.
You can create an IComparer an use
public virtual void Sort(
IComparer comparer
)
http://msdn.microsoft.com/en-us/library/0e743hdt.aspx
You can pass to Array.Sort either a delegate or a class implementing an interface that implements the custom comparison you need.
See some examples http://bytes.com/topic/c-sharp/answers/235679-two-dimension-array-sort.

SortedSet and SortedList fails with different enums

The whole story; I have some KeyValuePairs that I need to store in a session and my primary goal is to keep it small. Therefore I don't have the option of using many different collection. While the key is a different enum value of of a different enum type the value is always just a enum value of the same enum type. I have chosen a HashTable for this approach which content look like this (just many more):
// The Key-Value-Pairs
{ EnumTypA.ValueA1, MyEnum.ValueA },
{ EnumTypB.ValueB1, MyEnum.ValueB },
{ EnumTypC.ValueC1, MyEnum.ValueA },
{ EnumTypA.ValueA2, MyEnum.ValueC },
{ EnumTypB.ValueB1, MyEnum.ValueC }
At most I am running contains on that HashTable but for sure I also need to fetch the value at some point and I need to loop through all elements. That all works fine but now I have a new requirement to keep the order I have added them to the HashTable -> BANG
A HashTable is a map and that is not possible!
Now I thought about using a SortedList<object, MyEnum> or to go with more Data but slightly faster lookups and use a SortedSet<object> in addition to the HashTable.
Content below has been edited
The SortedList is implemented as
SortedList<Enum, MyEnum> mySortedList = new SortedList<Enum, MyEnum>();
the SortedSet is implemented as
SortedSet<Enum> mySortedSet = new SortedSet<Enum>();
The described Key - Value - Pairs are added to the sorted list with
void AddPair(Enum key, MyEnum value)
{
mySortedList.Add(key, value);
}
And for the SortedSett like this
void AddPair(Enum key)
{
mySortedSet.Add(key);
}
Both are failing with the exception:
Object must be the same type as the
enum
My question is: What goes wrong and how can I archive my goal?
Used Solution
I've decided to life with the downside
of redundant data against slower
lookups and decided to implement a
List<Enum> which will retain the
insert order parallel to my already
existing HashTable.
In my case I just have about 50-150
Elements so I decided to benchmark the
Hashtable against the
List<KeyValuePair<object,object>>
Therefore I have create me the
following helper to implement
ContainsKey() to the
List<KeyValuePair<object,object>>
static bool ContainsKey(this List<KeyValuePair<object, object>> list, object key)
{
foreach (KeyValuePair<object, object> p in list)
{
if (p.Key.Equals(key))
return true;
}
return false;
}
I inserted the same 100 Entries and
checked randomly for one of ten
different entries in a 300000 loop.
And... the difference was tiny so I
decided to go with the
List<KeyValuePair<object,object>>
I think you should store your data in an instance of List<KeyValuePair<Enum, MyEnum>> or Dictionary<Enum, MyEnum>.
SortedSet and SortedList are generic, but your keys are EnumTypeA/EnumTypeB, you need to specify the generic T with their base class(System.Enum) like:
SortedList<Enum, MyEnum> sorted = new SortedList<Enum, MyEnum>();
EDIT
Why you got this exception
SortedList and SortedSet use a comparer inside to check if two keys are equal. Comparer<Enum>.Default will be used as the comparer if you didn't specify the comparer in the constructor. Unfortunately Comparer<Enum>.Default isn't implemented as you expected. It throws the exception if the two enums are not the same type.
How to resolve the problem
If you don't want to use a List<KeyValuePair<Enum, MyEnum>> and insist using SortedLIst, you need to specify a comparer to the constructor like this:
class EnumComparer : IComparer<Enum>
{
public int Compare(Enum x, Enum y)
{
return x.GetHashCode() - y.GetHashCode();
}
}
var sorted = new SortedList<Enum, MyEnum>(new EnumComparer());
Btw, I think you need to obtain the "inserting order"? If so, List<KeyValuePair<K,V>> is a better choice, because SortedSet will prevent duplicated items.

Categories

Resources