tags:

views:

231

answers:

6

I've got a group of data that looks like this:

001 001 One
001 002 Two
001 003 Three

002 001 One
002 002 Two
002 003 Three

...

Now, certainly, I could create an array of string[x][y] = z, but this array has to be resizable, and i'd prefer to use the string representations of the indexers than convert to numeric. The reason is that i will need to look up the data by string, and i don't see the point in needless string->number conversions.

My first thought was this:

Dictionary<string, Dictionary<string, string>> data;

data = new Dictionary<string, Dictionary<string, string>>();

Dictionary<string, string> subdata = Dictionary<string, string>();

subdata.Add(key, string);
data.add(key2, subdata);

and this works, but is somewhat cumbersome. It also feels wrong and kludgy and not particularly efficient.

So what's the best way to store this sort of data in a collection?

I also thought of creating my own collection class, but I'd rather not if I don't have to. I'd rather just use the existing tools.

+1  A: 

Would a List<List<T>> work for you? Still kludgy, but better than dictionaries IMHO.


EDIT: What about a Dictionary<string,string> and mapping the two keys to a single string?

var data = new Dictionary<string,string>(StringComparer.Ordinal);

data[GetKey("002", "001")] = "One";

with

string GetKey(string a, string b) {
    return a + "\0" + b;
}
dtb
No. As I said, I would prefer to index them by string. And they may not always be sequential (might have a missing number in the middle).
Mystere Man
Oh, ok... I think your mention of multi-dimensional arrays threw us off (since those wouldn't have been indexed by Strings either).
jsight
+4  A: 

I think this really depends on what you are modelling here. If you're planning to use an object-oriented approach, you shouldn't be thinking of these as arbitrary items inside a data structure.

I'm guessing from looking at this that the first two columns are serving as a "key" for the other items. Define a simple struct, and create a dictionary of like so:

struct Key {
   public int Val1 { get; set; }
   public int Val2 { get; set; }
}

....

Dictionary<Key, string> values;

Obviously Key and the items inside it should be mapped to something closer to what you are representing.

Ryan Brunner
I'm not sure that does what you intend it to. Or maybe it does because you're using a struct (a value type). Are all key.Val1, key.Val2's equal if they contain the same data?
Mystere Man
I like this approach. You'd probably need to implement IComparer or Equals.
Randolpho
That is, Does this return true?new Key {Val1 = 1, Val2 = 2} == new Key {Val1 = 1, Val2 = 2};
Mystere Man
Yeah, sorry - you would need to implement IComparer and equals. I'm not 100% sure on the key evaluating, but I'm fairly sure it would work. Needs testing and all that jazz.
Ryan Brunner
This approach is very similar to the approaches specified by both myself and dtb in our edits, but it uses a structure rather than a string concatenation. The example above is incomplete; IComparer and/or Equals needs to be implemented/overridden, but other than that, it's a very solid approach.
Randolpho
GetHashcode would likely need to be overridden as well, given the fact that Dictionary will probably use a hashtable internally.
Randolpho
GetHashCode, sorry.
Randolpho
Yes, this looks like a better solution than your encapsulation approach in a new class. It solves the problem of getting access to the full complement of Dictionary functionality without writing a lot of wrapper code.
Mystere Man
+1  A: 

List<List<string>> is really your best bet in this case. But I agree, it's kludgy. Personally, I would create a custom class that implements a two-dimensional indexer and maybe use a List<List<T>> internally.

For example:

public class DynamicTwoDimensonalArray<T>
{
  private List<List<T>> Items = new List<List<T>>();

  public T this[int i1, int i2]
  {
    get
    {
      return Items[i1][i2];
    }
    set
    {
      Items[i1][i2] = value;
    }
  }  
}

This is a basic idea to get you going; clearly the setter needs to deal with bounds issues. But it's a start.

Edit:

No. As I said, I would prefer to index them by string. And they may not always be sequential (might have a missing number in the middle). - Mystere Man

Hmm... this is interesting. If that's the case, your best bet would be to create some sort of concatenation of the combination of the two indexers and use that as the key in a single-level dictionary. I would still use a custom class to make using the indexing easier. For example:

public class TwoDimensionalDictionary
{
  private Dictionary<string, string> Items = new Dictionary<string, string>();

  public string this[string i1, string i2]
  {
    get
    {
      // insert null checks here
      return Items[BuildKey(i1, i2)];
    }
    set
    {
      Items[BuildKey(i1, i2)] = value;
    }
  }
  public string BuildKey(string i1, string i2)
  {
    return "I1: " + i1 + " I2: " + i2;
  }  
}
Randolpho
This is an interesting solution, but i see some problems with edge cases. They could be solved though. For instance, if i1,i2 doesn't exist, it will throw an exception. no way to get access to the "Try" functionality or to do a if (Items.Keys.Exists(..)) kind of deal. so it would involve writing a lot of wrapper code, which i'm trying to avoid for maintenance reasons.
Mystere Man
Very true. As I said, it's a start, not a solution. That said, you'll have the same problems you mentioned even if you go the Dictionary<string, Dictionary<string, string>> route. Using a custom class to handle all of this will make your code much more maintainable.
Randolpho
+5  A: 

This is pretty common request, and most people end up writing some variation of a Tuple class. If you're using ASP.Net, you can utilize the Triple class that's already available, otherwise, write something like:

public class Tuple<T, T2, T3>
{
    public Tuple(T first, T2 second, T3 third)

    {
        First = first;
        Second = second;
        Third = third;
    }

    public T First { get; set; }
    public T2 Second { get; set; }
    public T3 Third { get; set; }

}

There's a generic three-tuple class, so you can create a new List<Tuple<string, string, string>>() and create your tuples and add them. Expand on that basic class with some indexing functionality and you're up up and away.

Edit: A list with a dictionary doesn't seem like the correct approach, because each dictionary is only holding one value. There is no multi-entry relationship between the key and values - there is simply one multi-part key and one associated value. The data is equivalent to a database row (or tuple!).

Edit2: Here's an indexable list class you could use for convenience.

    public class MyTupleList : List<Tuple<string, string, string>>
    {
        public Tuple<string, string, string> this[string first, string second]
        {
            get
            {
                return (this.Find(x => x.First == first && x.Second == second));
            }
            set
            {
                this[first, second] = value;
            }
        }
    }
womp
I had thought about this, but I couldn't see a good way to lookup items without walking the entire list and comparing values. You could do it with LINQ, but i can't be sure that this will always be used with C# 3 or greater. This would mean I'd have to add all the lookup searching code which, as you probably know, is not easy to get correct.
Mystere Man
I'll give you a full example in a minute, you'll love it ;)
womp
Thanks for the nice example. But, as I said in my comment, I can't be sure it will always be used with C# 3, so LINQ, while a nice solution to the problem, is not an option. And while an interesting exercise, I really don't want to spend my time writing good indexing code, that's what the existing collection classes are supposed to help us avoid ;)
Mystere Man
Well it depends on how flexible you need the key lookups. This is really just a generic version of what Ryan and John are proposing, which for your purposes are probably the best answers here, the indexing example is just icing, it's not even necessary. And it's simple to do the same thing without LINQ ;)
womp
I think i'm going to give you the answer anyways, because it's a good solution.
Mystere Man
you need the code to compile on different versions of the compiler or to run on different versions of the runtime? C#3 compiled DLL/programs run on the v2 of the runtime. Linq is syntactic suggar and is just compiled into regular static method calls. (All extension methods are syntactic sugar compiled into static method calls)
Rune FS
Compile on different versions of the compiler. This is generic, by definition, so it needs to be able to create the types at compile time.
Mystere Man
+3  A: 

Given a suitable Pair<A,B> class*, left as an exercise for the reader, you could use a Dictionary<Pair<string, string>, string>.

* A class with equality and hash code overrides, nothing terribly hard.

John Kugelman
A: 

If you are ever going to need to find z by given (x,y) (and not, for example, find all y by given x), then use this:

Dictionary<KeyValuePair<string, string>, string>

Otherwise, your dictionary is fine as is.

Pavel Minaev