2017-06-20 82 views
1

转换一个字符串列表 我有这个字符串:到 s字符 使用LINQ(清洁的方式)

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

留意。

我只是想使用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)吗?

+0

https://stackoverflow.com/questions/1297231/convert-string-to-int-in-one -line-code-using-linq/37033140#37033140 – Slai

回答

0

你可以做这样的:

List<int> numbers = input 
    .Split(',') 
    .Where(t => int.TryParse(t, out int a)) 
    .Select(int.Parse) 
    .ToList(); 
+4

如果有负数呢? – itsme86

+0

@ itsme86 OP在其原始代码中使用负数。 – Dai

+0

赶上@ itsme86。我会尽力解决它。 – Deadzone

-1
  • 你不需要调用.Split(...).ToList()作为String[]已经枚举。
  • 您可以在带有大括号的lambda中使用多个语句。
  • FindAll,ForEachRemoveAll方法不是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> 
+2

如果列表中有'-1',会发生什么情况? – NetMage

+0

@NetMage OP在他们的例子中使用了-1。 – Dai

+2

最好使用'Nullable'。 – kiziu

1

使用很好的扩展方法

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() 
+1

这是一个很好的解决方案,不太喜欢迭代器。 – Deadzone

+0

你的意思是'AsSingleton'?如前所述,您可以用'new [] {n}'替换它。 – NetMage

+4

这是“Singleton”这个名字的重载。当然有一个更好的名字。 – Enigmativity

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

它不返回List<int>,但我必须在某处画线。你可以列出一个列表。

+2

可能是一种静态方法,所以你不必新建一个类 –

+0

我喜欢你的解决方案。我的变体是:'var a = input.Split(',')。Where(str => int.TryParse(str,out number))。Select(_ => number);' – Gioce90

0

我更愿意做一个很好的辅助功能:

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

慢一些正则表达式的替代品:

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

不,不是数字。但是我喜欢你的第一个解决方案 – Gioce90

0

为什么一定要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

+0

这些测试几乎没有结论性,因为在你有很大的抖动的情况下,这样的低运行时间。一般来说,要避免这种测试应该运行至少几秒钟。如果多次运行你的小提琴,我有时会得到LINQ版本的速度提高了10倍,这确实显示了你设置测试的抖动。 – ckuri

0

这里有一个通用的LINQ的扩展,它采用了delegate。这将允许您传递返回bool的函数,同时“保留”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; 
      } 
     } 
    } 
}