簡體   English   中英

在C#中添加兩個不同長度的列表

[英]Add two Lists of different length in C#

List<double> a = new List<double>{1,2,3};
List<double> b = new List<double>{1,2,3,4,5};

a + b應該給我{2,4,6,4,5}

顯然我可以寫一個循環,但是有更好的方法嗎? 使用 LINQ?

您可以很容易地使用修改后的“zip”操作,但沒有內置任何內容。例如:

    static void Main() {
        var a = new List<int> { 1, 2, 3 };
        var b = new List<int> { 1, 2, 3, 4, 5 };
        foreach (var c in a.Merge(b, (x, y) => x + y)) {
            Console.WriteLine(c);
        }
    }
    static IEnumerable<T> Merge<T>(this IEnumerable<T> first,
            IEnumerable<T> second, Func<T, T, T> operation) {
        using (var iter1 = first.GetEnumerator())
        using (var iter2 = second.GetEnumerator()) {
            while (iter1.MoveNext()) {
                if (iter2.MoveNext()) {
                    yield return operation(iter1.Current, iter2.Current);
                } else {
                    yield return iter1.Current;
                }
            }
            while (iter2.MoveNext()) {
                yield return iter2.Current;
            }
        }
    }

使用 .NET 4.0 的Zip運算符:

var sums = b.Zip(a, (x, y) => x + y)
            .Concat(b.Skip(a.Count()));

如果您想概括這一點,請檢查哪個有更多元素並將其用作上面的“b”。

Enumerable.Range(0, new[] { a.Count, b.Count }.Max())
    .Select(n => a.ElementAtOrDefault(n) + b.ElementAtOrDefault(n));

我不得不稍微調整 Marc 的解決方案以供我使用以允許不同類型的列表,所以我想我會發布以防其他人需要它。

public static IEnumerable<TResult> Merge<TFirst,TSecond,TResult>(this IEnumerable<TFirst> first,
            IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> operation) {
    using (var iter1 = first.GetEnumerator()) {
        using (var iter2 = second.GetEnumerator()) {
            while (iter1.MoveNext()) {
                if (iter2.MoveNext()) {
                    yield return operation(iter1.Current, iter2.Current);
                } else {
                    yield return operation(iter1.Current, default(TSecond));
                }
            }
            while (iter2.MoveNext()) {
                yield return operation(default(TFirst),  iter2.Current);
            }
        }
    }
}

僅使用 Zip 的方法是:

b.Zip(a.DefaultIfEmpty(), (x,y) => x+y)

由於數字類型的默認值為0 ,因此您不需要任何額外的處理。

這個怎么樣:

List<double> doubles = Enumerable.Range(0, Math.Max(a.Count, b.Count))
    .Select(x => (a.Count > x ? a[x] : 0) + (b.Count > x ? b[x] : 0))
    .ToList();

以下是您的問題的解決方案。

List<double> a = new List<double>{1,2,3};
List<double> b = new List<double>{1,2,3,4,5};

List<double> sum = new List<double>();
int max = Math.Min(a.Count, b.Count);
for (int i = 0; i < max; i++){
    sum.Add(a[i] + b[i]);
}

if (a.Count < b.Count)
    for (int i = max i < b.Count)
        sum.Add(b[i]);
else
    for (int i = max i < a.Count)
    sum.Add(a[i]);

丑陋的 LINQ 解決方案:

var sum = Enumerable.Range(0, (a.Count > b.Count) ? a.Count : b.Count)
    .Select(i => (a.Count > i && b.Count > i) ? a[i] + b[i] : (a.Count > i) ? a[i] : b[i]);

在這種情況下,列表的長度是相同的還是不同的,這並不重要。 .NET 類庫沒有Enumerable.Zip方法來組合兩個序列(它只會出現在 .NET 4.0 中),無論哪種方式,您都需要類似的東西。 因此,您要么必須編寫一個循環,要么編寫自己的Zip (這仍然會涉及一個循環)。

有一些技巧可以在沒有循環的單個 LINQ 查詢中壓縮這一切,包括加入索引,但這些會非常緩慢而且毫無意義。

1和額外的23發生了什么? 如果您正在尋找不同的值:

var one = new List<int> { 1, 2, 3 };
var two = new List<int> { 1, 2, 3, 4, 5 };

foreach (var x in one.Union(two)) Console.Write("{0} ", x);

會給你1 2 3 4 5

如果您正在尋找附加到第一個列表的第二個列表,那么:

foreach(var x in one.Concat(two)) // ...

會給你1 2 3 1 2 3 4 5

編輯:哦,我明白了,您正在尋找一種Zip ,但它會返回額外的部分。 嘗試這個:

public static IEnumerable<V> Zip<T, U, V>(
    this IEnumerable<T> one,
    IEnumerable<U> two,
    Func<T, U, V> f)
{
    using (var oneIter = one.GetEnumerator()) {
        using (var twoIter = two.GetEnumerator()) {
            while (oneIter.MoveNext()) {
                twoIter.MoveNext();
                yield return f(oneIter.Current,
                    twoIter.MoveNext() ?
                        twoIter.Current :
                        default(U));
            }

            while (twoIter.MoveNext()) {
                yield return f(oneIter.Current, twoIter.Current);
            }
        }
    }
}

這是一個更像普通 zip 函數的函數,它不返回額外內容:

public static IEnumerable<V> Zip<T, U, V>(
    this IEnumerable<T> one,
    IEnumerable<U> two,
    Func<T, U, V> f)
{
    using (var oneIter = one.GetEnumerator()) {
        using (var twoIter = two.GetEnumerator()) {
            while (oneIter.MoveNext()) {
                yield return f(oneIter.Current,
                    twoIter.MoveNext() ?
                        twoIter.Current :
                        default(U));
            }
        }
    }
}

示例用法:

var one = new List<int>  { 1, 2, 3, 4, 5};
var two = new List<char> { 'h', 'e', 'l', 'l', 'o' };

foreach (var x in one.Zip(two, (a,b) => new {A = a, B =b }))
    Console.WriteLine("{0} => '{1}'", x.A, x.B);

結果是:

1 => 'h'
2 => 'e'
3 => 'l'
4 => 'l'
5 => 'o'

這里還有 3 個:

使列表大小相同,然后只需簡單的選擇。

(a.Count < b.Count ? a : b).AddRange(new double[Math.Abs(a.Count - b.Count)]);
var c = a.Select((n, i) => n + b[i]);

不要使它們的大小相同,而是通過最長的並檢查最短的范圍的結束(存儲 shortList.Count 以便於提高性能):

var longList = a.Count > b.Count ? a : b;
var shortList = longList == a ? b : a;
var c = longList.Select((n, i) => n + (shortList.Count > i ? shortList[i] : 0));

Take ,然后SkipUnion其余部分:

var c = a.Take(Math.Min(a.Count, b.Count))
         .Select((n, i) => n + b[i])
         .Union(a.Skip(Math.Min(a.Count, b.Count));

作為MarcDamian答案的組合,您可以只使用此代碼,它更適合生產:

public static class EnumerableExtensions
{
    public static IEnumerable<T> Merge<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, T> operation)
    {
        return Merge<T, T, T>(first, second, operation);
    }

    public static IEnumerable<TResult> Merge<T, TResult>(this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, TResult> operation)
    {
        return Merge<T, T, TResult>(first, second, operation);
    }

    public static IEnumerable<TResult> Merge<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> operation)
    {
        if (first == null) throw new ArgumentNullException(nameof(first));
        if (second == null) throw new ArgumentNullException(nameof(second));

        using (var iter1 = first.GetEnumerator())
        using (var iter2 = second.GetEnumerator())
        {
            while (iter1.MoveNext())
            {
                yield return iter2.MoveNext()
                    ? operation(iter1.Current, iter2.Current)
                    : operation(iter1.Current, default);
            }

            while (iter2.MoveNext())
            {
                yield return operation(default, iter2.Current);
            }
        }
    }
}

您可以使用 c 壓縮 b,其中 c 具有相同的大小:

var c = a.Concat(Enumerable.Repeat(0, b.Length - a.Length)); 

我使用循環的實現:

List<double> shorter, longer;
if (a.Count > b.Count)
{
    shorter = b; longer = a
}
else
{
    shorter = a; longer = b;
}

List<double> result = new List<double>(longer);
for (int i = 0; i < shorter.Count; ++i)
{
     result[i] += shorter[i];
}

請注意,這是對我的其他答案的補充,它解釋了為什么在 3.5 中無法通過 LINQ 完成此操作。 它只是試圖提供最短且最易讀的基於循環的實現。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM