使用 LINQ(更简洁的方式)转换 List<int> 中的字符串

Convert a string in a List<int> using LINQ (cleaner way)

我有这个字符串:

string input = "1,2,3,4,s,6";

注意 s 字符。

我只想使用 LINQ 将此字符串转换为 List<int>。我最初是这样尝试的:

var myList = new List<int>();
input.Split(',').ToList().ForEach(n =>
    myList.Add(int.TryParse(n, out int num) ? num : -1)
);
lista.RemoveAll(e => e == -1);

但我宁愿没有任何 -1 而不是无数字字符。

所以现在我试试这个:

var myList = new List<int>();
input.Split(',').ToList()
    .FindAll(n => int.TryParse(n, out int _))
    .ForEach(num => myList.Add(int.Parse(num)));

我更喜欢这个,但是解析发生两次真的很遗憾(首先是TryParse,然后是Parse)。但是,据我了解,TryParse 中的 out 变量是无用的(或者不是?)。

你有其他人建议(使用 LINQ)吗?

你可以这样做:

List<int> numbers = input
    .Split(',')
    .Where(t => int.TryParse(t, out int a))
    .Select(int.Parse)
    .ToList();
  • 您不需要调用 .Split(...).ToList(),因为 String[] 已经是可枚举的了。
  • 您可以在带大括号的 lambda 中使用多个语句。
  • FindAllForEachRemoveAll 方法不是 Linq 方法,它们是 List<T> 的成员。他们的 Linq 等价物是 Where.

像这样:

List<Int32> numbers = "1,2,3,4,s,6"
    .Split(',')
    .Select( s => { Int32 val; return Int32.TryParse( s, NumberStyles.Integer, CultureInfo.InvariantCulture, out val ) ? val : -1 } )
    .Where( n => n != -1 )
    .ToList();

您可以使用辅助方法使其更简洁:

static Int32 Parse(String s) {
    Int32 ret;
    if( Int32.TryParse( s, NumberStyles.Integer, CultureInfo.InvariantCulture, out ret ) ) {
        return ret;
    }
    return -1;
}

变为:

List<Int32> numbers = "1,2,3,4,s,6"
    .Split(',')
    .Select( s => Parse( s ) )
    .Where( n => n != -1 )
    .ToList();

如果你不想保留 -1 那么你可以使用可为空的整数:

static Int32? Parse(String s) {
    Int32 ret;
    if( Int32.TryParse( s, NumberStyles.Integer, CultureInfo.InvariantCulture, out ret ) ) {
        return ret;
    }
    return null;
}

List<Int32> numbers = "1,2,3,4,s,6"
    .Split(',')                     // String to String[]
    .Select( s => Parse( s ) )      // String[] to IEnumerable<Int32?>
    .Where( n => n != null )        // filter out nulls
    .Select( n => n.Value )         // IEnumerable<Int32?> to IEnumerable<Int32>
    .ToList();                      // IEnumerable<Int32> to List<Int32>

使用不错的扩展方法

public static IEnumerable<T> AsSingleton<T>(this T source) {
    yield return source;
}

(如果愿意,您可以将其替换为 new[] { n }

input.Split(',').SelectMany(s => Int32.TryParse(s, out var n) ? n.AsSingleton()  : Enumerable.Empty<int>()).ToList()
public class ParsesStringsToIntsWithLinq
{
    public IEnumerable<int> Parse(string input)
    {
        var i = 0;
        return (from segment in input.Split(',')
            where int.TryParse(segment, out i) 
            select i);
    }
}

[TestClass]
public class Tests
{
    [TestMethod]
    public void IgnoresNonIntegers()
    {
        var input = "1,2,3,4,s,6";
        var output = new ParsesStringsToIntsWithLinq().Parse(input);
        Assert.IsTrue(output.SequenceEqual(new []{1,2,3,4,6}));
    }
}

它没有 return 和 List<int> 但我必须在某处划清界限。你可以列一个清单。

我更喜欢做一个不错的辅助函数:

Func<string, int?> tryParse = s => int.TryParse(s, out int n) ? (int?)n : null;

那么解析起来就很简单了:

string input = "1,2,3,4,s,6";

List<int> myList =
    input
        .Split(',')
        .Select(s => tryParse(s))
        .Where(n => n.HasValue)
        .Select(n => n.Value)
        .ToList();

这给出:

1 
2 
3 
4 
6 
int i = 0; 
var myList = (from s in input.Split(',') where int.TryParse(s, out i) select i).ToList();

如果数字始终是单个 ASCII 数字:

var myList = "1,2,3,4,s,6".Select(c => c ^ 48).Where(i => i < 10).ToList();

一些较慢的 RegEx 替代品很有趣:

var myList2 = Regex.Split("1,2,3,4,s,6", "[^0-9]+").Select(int.Parse).ToList(); // if the string starts and ends with digits

var myList3 = Regex.Replace("1,2,3,4,s,6", "[^0-9]+", " ").Trim().Split(' ').Select(int.Parse).ToList();

var myList4 = Regex.Matches("1,2,3,4,s,6", "[0-9]+").Cast<Match>().Select(m => int.Parse(m.Value)).ToList();

为什么一定要是 LINQ?

尝试:

//Come up a better name...
public static List<int> ConvertToIntListNoLinq(string input)
{
    List<int> output = new List<int>();
    foreach(string s in input.Split(','))
    {
        if(int.TryParse(s, out int result))
        {
            output.Add(result);
        }               
    }
    return output;
}

Fiddle

这是一个通用的 LINQ 扩展,它使用 delegate。这将允许您传入返回 bool 的函数,而 "retaining" 返回 out 变量的结果(如 int.TryParse)。


用法:

string input = "1,2,3,4,s,6";
List<int> myList = input.Split(',').SelectTry<string, int>(int.TryParse).ToList();

代码:

using System.Collections.Generic;

public static class LINQExtensions
{
    public delegate bool TryFunc<TSource, TResult>(TSource source, out TResult result);

    public static IEnumerable<TResult> SelectTry<TSource, TResult>(
        this IEnumerable<TSource> source, TryFunc<TSource, TResult> selector)
    {
        foreach (TSource item in source)
        {
            TResult result;
            if (selector(item, out result))
            {
                yield return result;
            }
        }
    }
}

我认为这也是一种干净的方式。尽管它使用了那个额外的变量,但我们得到的好处是它是干净且易于理解的。

string ids = "2,4,2,4,5,s"
const int inValidInt = -99;
var ids = ids.Split(',')
.Select(id =>
{
    int parsedId = int.TryParse(id, out parsedId) ? parsedId : inValidInt;
    return parsedId;
})
.Where(x => x != inValidInt).ToList();