views:

84

answers:

3

The structure I am trying to achieve is a composite Dictionary key which is item name and item displayname and the Dictionary value being the combination of n strings

So I came up with

var pages = new Dictionary<string[], StringBuilder>()
{
    { new string[] { "food-and-drink", "Food & Drink" }, new StringBuilder() },
    { new string[] { "activities-and-entertainment", "Activities & Entertainment" }, new StringBuilder() }
};

foreach (var obj in my collection)
{
    switch (obj.Page)
    {
        case "Food":
        case "Drink":
            pages["KEY"].Append("obj.PageValue");
            break;
        ...
    }
}

The part I am having trouble with is accessing the Dictionary Key pages["KEY"]

How do I target the Dictionary Key whose value at [0] == some value?

Hope that makes sense

+2  A: 

This won't work. Arrays compare equality by their reference, not their content.

You have to create your own helper structure which has both strings and overrides both Equals() and GetHashCode() to use the first string and maybe even has an implicit conversion operator from string, or supply a custom IEqualityComparer<string[]> to the dictionary which does the work of comparing the keys.

Edit - Code Sample:

public struct MyKey: IEquatable<MyKey> {
    public static implicit operator MyKey(string key) {
    return new MyKey(key, key);
}

    private readonly string key;
    private readonly string display;

    public MyKey(string key, string display) {
        this.key = key;
        this.display = display;
    }

    public override bool Equals(object other) {
        if (other is MyKey) {
            return Equals((MyKey)other);
        }
        return false;
    }

    public bool Equals(MyKey other) {
        return other.key == key;
    }

    public override int GetHashCode() {
        if (key != null) {
            return key.GetHashCode();
        }
        return 0;
    }

    public string Key {
        get {
            return key;
        }
    }

    public override string ToString() {
        return display ?? string.Empty;
    }
}
Lucero
A: 

I would create a new extension method on string[] to return a hash of the contents of the array that could then be used as a key:

    public static int GetKey(this string[] array)
    {
        var builder = new System.Text.StringBuilder();
        foreach (var value in array)
        {
            builder.Append(value);
        }
        return builder.ToString().GetHashCode();
    }

Use:

var pages = new Dictionary<int, StringBuilder>();
var array = new string[] { "food-and-drink", "Food & Drink" };
pages.Add(array.GetKey(), new StringBuilder());
James Westgate
And what if there's a hash collision? Also, how does this help to perform the lookup by the first item?
kvb
Same very small chance of hash collision than if you just used a string for a key. The GetHashCode method is used when using any object as a key to a collection. The point is to use the items in the array to generate a key.
James Westgate
+2  A: 

Based on some comments:

Juliet: It sounds to me like you're abusing the dictionary. Why do you need a string[] as a key? What's wrong with using "food-and-drink" as a key?

OP: I am creating a set of nested pages in the CMS by looping over the pages Dictionary, the "Food & Drink" part would be used as the display name for the page as the CMS (Sitecore) doesn't allow certain characters in the items name Otherwise when creating the pages I would have to do if page name == x display name = y, which I don't want to do

You have an architectural problem, and a key with a string array is definitely the wrong way to solve it. Never use a string[] or object[] for ad hoc groups of data, create a class instead.

Additionally, if your dictionary is keyed on one piece of data, then make it keyed on one piece of data. All other data parts should be stored in your value.

So let's start with this:

public class Page
{
    public readonly string PageTitle;
    public readonly StringBuilder Content;

    public Page(string pageTitle)
    {
        this.PageTitle = pageTitle;
        this.Content = new StringBuilder();
    }
}

And use it like this:

var pages = new Dictionary<string, Page>()
{
    { "food-and-drink", new Page("Food & Drink") },
    { "activities-and-entertainment", new Page("Activities & Entertainment")
};

foreach (var obj in myCollection)
{
    pages[obj.Page].Content.Append(obj.PageValue);
}

Odds are, this isn't the perfect approach for what you're actually trying to do, but I think its a step up from the weirdness of storing composite data as a dictionary key.

Juliet
I don't know how I didn't see this simple solution straight away, probably a case of being too close to some code for too long
Nick Allen - Tungle139