Convert a string in a List using LINQ (cleane

2019-06-05 10:01发布

I have this string:

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

Pay attention to the s character.

I just want to convert this string in a List<int> using LINQ. I initially tried in this way:

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

But I prefer not have any -1 instead of a no-number characters.

So now I try with this:

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

I prefer this, but is really a shame that the parsing happening two times (TryParse at first and then Parse). But, from what I understand, the out variable in TryParse is useless (or not?).

Have you others suggests (using LINQ)?

8条回答
We Are One
2楼-- · 2019-06-05 10:33

Here's a generic LINQ extension, which utilizes a delegate. This will allow you to pass in a function returning a bool, while "retaining" the result of the out variable (like int.TryParse).


Usage:

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

Code:

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;
            }
        }
    }
}
查看更多
放我归山
3楼-- · 2019-06-05 10:40
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}));
    }
}

It doesn't return a List<int> but I have to draw the line somewhere. You can make a list out of it.

查看更多
相关推荐>>
4楼-- · 2019-06-05 10:42

You can do it like this:

List<int> numbers = input
    .Split(',')
    .Where(t => int.TryParse(t, out int a))
    .Select(int.Parse)
    .ToList();
查看更多
甜甜的少女心
5楼-- · 2019-06-05 10:46

Why does it have to be LINQ?

Try:

//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

查看更多
Rolldiameter
6楼-- · 2019-06-05 10:48
  • You don't need to call .Split(...).ToList() as String[] is already enumerable.
  • You can use multiple statements in a lambda with braces.
  • The FindAll, ForEach and RemoveAll methods are not Linq methods, they're members of List<T>. Their Linq equivalent is Where.

Like so:

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

You can make it more concise with a helper method:

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

Becomes:

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

If you don't want to reserve -1 then you can use nullable ints:

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>
查看更多
相关推荐>>
7楼-- · 2019-06-05 10:52

Using a nice extension method

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

(which you can replace with new[] { n } if preferred)

input.Split(',').SelectMany(s => Int32.TryParse(s, out var n) ? n.AsSingleton()  : Enumerable.Empty<int>()).ToList()
查看更多
登录 后发表回答