Объединение / объединение массивов в C # - PullRequest
5 голосов
/ 13 февраля 2009

У меня есть два или более массивов - один с идентификаторами, один или несколько со строковыми значениями. Я хочу объединить их в хеш-таблицу, чтобы я мог искать значения по ID.

Следующая функция работает, но более короткая и сладкая версия (LINQ?) Была бы хороша:

Dictionary<int, string[]> MergeArrays( IEnumerable<int> idCollection,
                                       params IEnumerable<string>[] valueCollections )
{
    var dict = new Dictionary<int, string[]>();

    var idL = idCollection.Count();
    while ( idL-- > 0 )
    {
        dict[idCollection.ElementAt( idL )] = new string[valueCollections.Length];

        var vL = valueCollections.Length;
        while ( vL-- > 0 )
            dict[idCollection.ElementAt( idL )][vL] = valueCollections[vL].ElementAt( idL );
    }

    return dict;
}

Есть идеи?

Ответы [ 5 ]

3 голосов
/ 13 февраля 2009

Это очень неэффективно на данный момент - все эти вызовы ElementAt могут проходить всю последовательность (насколько им нужно) каждый раз. (Это зависит от реализации последовательности.)

Однако я совсем не уверен, что даже понимаю, что делает этот код (использование циклов foreach почти наверняка сделает его более понятным, как и итерация вперед, а не назад. Не могли бы вы дать пример ввода?

РЕДАКТИРОВАТЬ: Хорошо, я думаю, что я вижу, что здесь происходит; вы эффективно разворачиваете ValueCollections. Я подозреваю, что вы захотите что-то вроде:

static Dictionary<int, string[]> MergeArrays(
    IEnumerable<int> idCollection,
    params IEnumerable<string>[] valueCollections)
{
    var valueCollectionArrays = valueCollections.Select
         (x => x.ToArray()).ToArray();
    var indexedIds = idCollection.Select((Id, Index) => new { Index, Id });

    return indexedIds.ToDictionary(x => Id, 
        x => valueCollectionArrays.Select(array => array[x.Index]).ToArray());
}

Это довольно уродливо, хотя. Если вы можете сделать idCollection массивом для начала, это было бы откровенно проще.

РЕДАКТИРОВАТЬ: Хорошо, предполагая, что мы можем использовать массивы вместо:

static Dictionary<int, string[]> MergeArrays(
    int[] idCollection,
    params string[][] valueCollections)
{
    var ret = new Dictionary<int, string[]>();
    for (int i=0; i < idCollection.Length; i++)
    {
         ret[idCollection[i]] = valueCollections.Select
             (array => array[i]).ToArray();
    }
    return ret;
}

Я исправил (надеюсь) ошибку в первой версии - я запутался в том, какой бит значений является массивом, а какой нет. Вторая версия не так декларативна, но, на мой взгляд, она понятнее.

2 голосов
/ 13 февраля 2009

Как насчет:

        public static Dictionary<int, string[]> MergeArrays2(IEnumerable<int> idCollection,
        params IEnumerable<string>[] valueCollections)
    {
        var dict = new Dictionary<int, string[]>();
        var valEnums = (from v in valueCollections select v.GetEnumerator()).ToList();
        foreach (int id in idCollection)
        {
            var strings = new List<string>();
            foreach (var e in valEnums)
                if (e.MoveNext())
                    strings.Add(e.Current);
            dict.Add(id, strings.ToArray());
        }
        return dict;
    }

или небольшое редактирование ответа скита (у меня это не сработало):

        static Dictionary<int, string[]> MergeArrays_Skeet(IEnumerable<int> idCollection,params IEnumerable<string>[] valueCollections)
    {
        var valueCollectionArrays = valueCollections.Select(x=>x.ToArray()).ToArray();
        var indexedIds = idCollection.Select((Id, Index) => new { Index, Id });
        return indexedIds.ToDictionary(x => x.Id,x => valueCollectionArrays.Select(array => array[x.Index]).ToArray());
    }
1 голос
/ 13 февраля 2009

Вот немного элегантности. Это длиннее, чем мне нравится, но это очень удобно.

    public Dictionary<int, string[]> MergeArrays(
        IEnumerable<int> idCollection,
        params IEnumerable<string>[] valueCollections
            )
    {
        Dictionary<int, int> ids = idCollection
            .ToDictionaryByIndex();
        //
        Dictionary<int, List<string>> values =
            valueCollections.Select(x => x.ToList())
            .ToList()
            .Pivot()
            .ToDictionaryByIndex();
        //
        Dictionary<int, string[]> result =
            ids.ToDictionary(
                z => z.Value,
                z => values[z.Key].ToArray()
            );

        return result;
    }

А вот вспомогательные методы, которые я использовал.

   public static List<List<T>> Pivot<T>
        (this List<List<T>> source)
    {
        return source
            .SelectMany((it) =>
              it.Select((t, i) => new { t, i })
            )
            .GroupBy(z => z.i)
            .Select(g => g.Select(z => z.t).ToList())
            .ToList();
    }

    public static Dictionary<int, T> ToDictionaryByIndex<T>
        (this IEnumerable<T> source)
    {
        return source
            .Select((t, i) => new { t, i })
            .ToDictionary(z => z.i, z => z.t);
    }

Отказ от ответственности: если вы позвоните в Pivot с непрямоугольной структурой, я не знаю / все равно, что произойдет.

1 голос
/ 13 февраля 2009

Если я что-то упустил, вам не нужен код для дублирования массивов - они сами по себе являются объектами и не исчезнут неожиданно.

Используйте Select(), чтобы объединить значения, используя анонимный класс, затем ToDictionary(), чтобы собрать их.

Попробуйте это:

    IDictionary<int, IEnumerable<string>> MergeArrays2(
        IEnumerable<int> idCollection,
        params IEnumerable<string>[] valueCollections)
    {
        var values = valueCollections.ToList();
        return idCollection.Select(
            (id, index) => new { Key = id, Value = values[index] })
            .ToDictionary(x => x.Key, x => x.Value);
    }

Обратите внимание, что возвращаемый тип использует IEnumerable вместо строки [] - ИМХО, вы найдете это более гибким, чем использование массивов.

Кроме того, тип возвращаемого значения использует интерфейс.

Обновлено : Джон Скит указал (см. Комментарий ниже), что массивы изменчивы, и это может быть проблемой. Ниже приведено простое изменение для создания новых массивов:

    IDictionary<int, IEnumerable<string>> MergeArrays2(
        IEnumerable<int> idCollection,
        params IEnumerable<string>[] valueCollections)
    {
        var values = valueCollections.ToList();
        return idCollection.Select(
            (id, index) => new 
            { 
                Key = id, 
                Value = values[index].ToArray()  // Make new array with values
            })
            .ToDictionary(x => x.Key, x => x.Value);
    }
0 голосов
/ 13 февраля 2009

Это не совсем так, как вы просили, но объединяет два перечислимых числа во множество пар. Превратить перечисляемый результат в словарь довольно просто.

public static class ExtensionMethods
{
    public static IEnumerable<Pair<TOuter, TInner>> InnerPair<TInner, TOuter>(this IEnumerable<TOuter> master,
                                                                         IEnumerable<TInner> minor)
    {
        if (master == null)
            throw new ArgumentNullException("master");
        if (minor == null)
            throw new ArgumentNullException("minor");
        return InnerPairIterator(master, minor);
    }

    public static IEnumerable<Pair<TOuter, TInner>> InnerPairIterator<TOuter, TInner>(IEnumerable<TOuter> master,
                                                                                 IEnumerable<TInner> minor)
    {
        IEnumerator<TOuter> imaster = master.GetEnumerator();
        IEnumerator<TInner> iminor = minor.GetEnumerator();
        while (imaster.MoveNext() && iminor.MoveNext())
        {
            yield return
                new Pair<TOuter, TInner> { First = imaster.Current, Second = iminor.Current };
        }
    }
}


public class Pair<TFirst, TSecond>
{
    public TFirst First { get; set; }
    public TSecond Second { get; set; }
}
...