2017-06-20 76 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; 
      } 
     } 
    } 
}