Элегантный способ перехода от списка объектов к словарю с двумя свойствами - PullRequest
24 голосов
/ 01 июня 2009

Кажется, я пишу этот код снова и снова и хотел посмотреть, есть ли лучший способ сделать это более обобщенно.

Я начинаю со списка объектов Foo

Foo[] foos = GenerateFoos();

Я хочу создать словарь, в котором ключ и значение являются свойствами Foo

например:

Dictionary<string, string> fooDict = new Dictionary<string, string>():
foreach (Foo foo in foos)
{
    fooDict[foo.Name] = foo.StreetAddress;
}

в любом случае можно написать этот код в общем виде, поскольку он выглядит как базовый шаблон, в котором есть массив объектов, свойство ключа, свойство value и словарь.

Есть предложения?

Я использую VS 2005 (C #, 2.0)

Ответы [ 4 ]

74 голосов
/ 01 июня 2009

С LINQ:

var fooDict = foos.ToDictionary(x=>x.Name,x=>x.StreetAddress);

(и да, fooDict - это Dictionary<string, string>)


изменить, чтобы показать боль в VS2005:

Dictionary<string, string> fooDict =
    Program.ToDictionary<Foo, string, string>(foos,
        delegate(Foo foo) { return foo.Name; },
        delegate(Foo foo) { return foo.StreetAddress; });

где у вас есть (в Program):

public static Dictionary<TKey, TValue> ToDictionary<TSource, TKey, TValue>(
    IEnumerable<TSource> items,
    Converter<TSource, TKey> keySelector,
    Converter<TSource, TValue> valueSelector)
{
    Dictionary<TKey, TValue> result = new Dictionary<TKey, TValue>();
    foreach (TSource item in items)
    {
        result.Add(keySelector(item), valueSelector(item));
    }
    return result;
}
7 голосов
/ 01 июня 2009

Если вы используете framework 3.5, вы можете использовать расширение ToDictionary:

Dictionary<string, string> fooDict = foos.ToDictionary(f => f.Name, f => f.StreetAddress);

Для фреймворка 2.0 код достаточно прост.

Вы можете немного улучшить производительность, указав емкость для словаря при его создании, чтобы он не занимался перераспределением при заполнении:

Dictionary<string, string> fooDict = new Dictionary<string, string>(foos.Count):
2 голосов
/ 01 июня 2009

Без LINQ нет встроенных помощников для этого. Вы можете написать один, хотя:

// I forget if you need this delegate definition -- this may be already defined in .NET 2.0
public delegate R Func<T,R>(T obj);
public static Dictionary<K,V> BuildDictionary<T,K,V>(IEnumerable<T> objs, Func<T,K> kf, Func<T,V> vf)
{
    Dictionary<K,V> d = new Dictionary<K,V>();
    foreach (T obj in objs)
    {
        d[kf(obj)] = vf(obj);
    }
    return d;
}

Dictionary<string, string> fooDict = BuildDictionary(foos, new Func<Foo,string>(delegate(Foo foo) { return foo.Name; }), new Func<Foo,string>(delegate(Foo foo) { return foo.StreetAddress; }));

Это выглядит не так элегантно, как ответы на основе LINQ, не так ли ...

1 голос
/ 01 июня 2009

Вот решение, совместимое с .net 2.0, которое использует System.Web.UI.Databinder для отражения имени свойства - вы теряете проверку типов во время компиляции.

        public static Dictionary<string, string> ToDictionary<T>(List<T> list, string keyName, string valueName)
    {
        Dictionary<string, string> outputDictionary = new Dictionary<string, string>();
        foreach (T item in list)
        {
            string key = Eval<T, string>(item, keyName);
            string value = Eval<T, string>(item, valueName);
            output[key] = value;
        }

        return outputDictionary;
    }

    public static TOut Eval<TIn, TOut>(TIn source, string propertyName)
    {
        object o = DataBinder.GetPropertyValue(source, propertyName);
        if (o is TOut)
            return (TOut)o;

        return default(TOut);
    }

Вы бы позвонили следующим образом:

Dictionary<string, string> fooDict = ToDictionary(foos, "Name", "StreetAddress");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...