在C#中加入/合并数组

时间:2009-02-13 10:04:30

标签: c# .net linq arrays c#-3.0

我有两个或更多数组 - 一个有ID,一个或多个有字符串值。我想将它们合并到一个哈希表中,这样我就可以按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 个答案:

答案 0 :(得分:3)

目前这是非常无效的 - 所有这些对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;
}

我已经纠正了(希望)第一个版本中的错误 - 我对哪个值是数组而哪个不是数组感到困惑。第二个版本不是声明性的,但我认为它更清楚,个人。

答案 1 :(得分:2)

怎么样:

        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());
    }

答案 2 :(得分:1)

除非我遗漏了某些东西,否则你不需要代码来复制数组 - 它们本身就是对象,并且不会意外消失。

使用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而不是string [] - 恕我直言,你会发现这比使用数组更灵活。

此外,返回类型使用的是接口。

更新:John Skeet提出了一个很好的观点(见下面的评论)数组是可变的,这可能是一个问题。请参阅下文,以便轻松更改以创建新阵列:

    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);
    }

答案 3 :(得分:1)

这里有一些优雅。它比我喜欢的时间长,但它是可以探索的。

    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,我不知道/关心会发生什么。

答案 4 :(得分:0)

这并不像你要求的那样一般,但它将两个枚举连接成一个可枚举的对。将该结果可枚举为字典非常简单。

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; }
}