简体   繁体   English

LINQ 如何在收集结束时删除空元素

[英]LINQ How to Remove Empty Elements at End of Collection

I have an array of strings that has a fixed size of 10. Some elements may be null .我有一个固定大小为 10 的字符串数组。某些元素可能是null I want to use LINQ to remove the elements with the value null from the array, but only at the end.我想使用 LINQ 从数组中删除值为null的元素,但仅在最后。

Input:输入:

"Hello", null, "World", null, null, null, null, null, null, null

Output: Output:

"Hello", null, "World"

So the output array contains all elements except for the trailing ones containing null .所以 output 数组包含除了包含null的尾随元素之外的所有元素。

I thought of reversing the IEnumerable, then doing a SkipWhile and reversing it again.我想反转 IEnumerable,然后做一个 SkipWhile 并再次反转它。 This would be possible and would be not much of a performance penalty in my case, but I am curious if there's a more elegant solution.这是可能的,在我的情况下不会对性能造成太大影响,但我很好奇是否有更优雅的解决方案。

If your collection is a List you can use FindLastIndex :如果您的收藏是List ,您可以使用FindLastIndex

var x = new List<string> { "Hello", null, "World", null, null, null, null, null, null, null};
x = x.Take(x.FindLastIndex(s => s != null) + 1).ToList();

If you are working with another collection type you can try something like this:如果您正在使用其他集合类型,您可以尝试以下操作:

x.Take(x.Select((s, index) => (s, index))
    .Aggregate(-1, (acc, curr) => curr.s != null ? curr.index : acc) + 1)

So it is definitely not an elegant one.所以它绝对不是一个优雅的。

If your collection is guaranteed to have at least one not null element you can make it a little bit prettier:如果保证您的收藏至少有一个非 null 元素,您可以让它更漂亮一点:

x.Take(x.Select((s, index) => (s, index)).Last(t => t.s != null).index + 1)

Or just hide the ugliness by writing your own extension method which will implement FindLastIndex logic for IEnumerable .或者只是通过编写自己的扩展方法来隐藏丑陋,该方法将为IEnumerable实现FindLastIndex逻辑。

For a different LINQ variant that works for any enumerable ( string[] , List<string> ) you can try the following which uses Reverse and SkipWhile :对于适用于任何可枚举( string[]List<string> )的不同 LINQ 变体,您可以尝试以下使用ReverseSkipWhile

using System.Linq;

string[] strings = { "Hello", null, "World", null, null, null, null, null, null };
var result = strings.Reverse()
                    .SkipWhile(c => c is null)
                    .Reverse()
                    .ToArray();

Note that this is definitely not the most performant, I'm just offering a different version than those already posted.请注意,这绝对不是性能最高的,我只是提供与已经发布的版本不同的版本。

Since you have an "array of strings", I assume you mean literally a string[] and not a List<string> .由于您有一个“字符串数组”,我假设您的意思是字面上的string[]而不是List<string> As an alternative to LINQ you can use operations over the array with Array.FindLastIndex + AsSpan + ToArray (note that all solutions here forward assume that it's possible for your array to be all null s):作为 LINQ 的替代方案,您可以使用Array.FindLastIndex + AsSpan + ToArray对数组进行操作(请注意,此处的所有解决方案都假定您的数组可能都是null s):

string[] strings = { "Hello", null, "World", null, null, null, null, null, null };
var idx = Array.FindLastIndex(strings, c => c != null);

var result = idx >= 0 ? strings.AsSpan(0, idx + 1).ToArray() 
                      : Array.Empty<string>();

If Span<T> is not available to you, we could combine with LINQ's Take :如果您无法使用Span<T> ,我们可以结合 LINQ 的Take

var idx = Array.FindLastIndex(strings, c => c != null);
var result = idx >= 0 ? strings.Take(idx + 1).ToArray() 
                      : Array.Empty<string>();

Or without LINQ:或者没有 LINQ:

var idx = Array.FindLastIndex(strings, c => c != null);
var result = Array.Empty<string>();
if (idx >= 0)
{
   result = new string[idx +1 ];
   Array.Copy(strings, 0, result, 0, idx + 1);
} 

If you have later C# features, we can use System.Range / System.Index and create a sub-array of the existing array:如果您以后有 C# 功能,我们可以使用System.Range / System.Index并创建现有数组的子数组:

string[] strings = { "Hello", null, "World", null, null, null, null, null, null };

var idx = Array.FindLastIndex(strings, c => c != null);  
var result = idx >= 0 ? strings[..(idx + 1)] 
                      : Array.Empty<string>();

Or as a (kind of hard to read) one-liner if that's what you are after:或者作为(有点难以阅读的)单行如果那是你所追求的:

var result = Array.FindLastIndex(strings, c => c != null) is int idx && idx >= 0 ? strings[..(idx + 1)] : Array.Empty<string>();

Removing Elements from Array从数组中删除元素

You're title asks to remove elements.您的标题要求删除元素。 While I'm fairly sure you're looking to create a new collection (given your mention of LINQ), I figured I'd point out that you can't really remove elements from an array--they are fixed in size.虽然我相当确定您正在寻找创建一个新集合(鉴于您提到 LINQ),但我想我会指出您不能真正从数组中删除元素——它们的大小是固定的。 However you can make use of Array.Resize to "crop" your array:但是,您可以使用Array.Resize来“裁剪”您的数组:

var idx = Array.FindLastIndex(strings, c => c != null); 
if (idx >= 0)
   Array.Resize(ref strings, idx + 1);

Here's a method that doesn't require allocations, random-access collections, or iterating the collection multiple times.这是一种不需要分配、随机访问 collections 或多次迭代集合的方法。

public static class EnumerableExtensions
{
    public static IEnumerable<T> TrimEnd<T>(this IEnumerable<T> col)
    {
        int nulls = 0;
        foreach (var e in col)
        {
            if ( e == null || e.Equals(default(T)) )
            {
                nulls += 1;
            }
            else
            {
                while (nulls > 0)
                {
                    nulls -= 1;
                    yield return default(T);
                }
                yield return e;
            }

        }

    }
}

You can get last index of not null or empty value from the list then get all values before last index您可以从列表中获取非null的最后一个索引或值,然后获取最后一个索引之前的所有值

List<string> data = new List<string> { "Hello", null, "World", null, null, null, null, null, null, null };
int lastIndex = data.FindLastIndex(a => a != null);
data = data.Where((value, index) => index <= lastIndex).ToList();

Result: "Hello", null, "World"结果: "Hello", null, "World"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM