2017-06-20 6 views
1

J'ai cette chaîne:Convertir une chaîne dans une liste <int> LINQ (de façon plus propre)

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

Faites attention au caractère s.

Je veux juste convertir cette chaîne dans un List<int> utilisant LINQ. J'ai d'abord essayé de cette façon:

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

Mais je préfère ne pas avoir de -1 au lieu d'un caractère non-numéro.

Alors maintenant, j'essayer avec ceci:

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

Je préfère, mais est vraiment dommage que l'analyse syntaxique qui se passe deux fois (TryParse au début, puis Parse). Mais, d'après ce que je comprends, la variable out dans TryParse est inutile (ou pas?).

D'autres vous suggère (utilisant LINQ)?

+0

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

Répondre

0

Vous pouvez le faire comme ceci:

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

Et s'il y a un nombre négatif? – itsme86

+0

@ itsme86 L'OP utilisait des nombres négatifs dans leur code d'origine. – Dai

+0

Bonne capture @ itsme86. Je vais essayer de le réparer. – Deadzone

-1
  • Vous n'avez pas besoin d'appeler .Split(...).ToList() comme String[] est déjà dénombrable.
  • Vous pouvez utiliser plusieurs instructions dans un lambda avec des accolades.
  • Les méthodes FindAll, ForEach et RemoveAll ne sont pas des méthodes Linq, elles sont membres de List<T>. Leur équivalent Linq est Where.

comme ceci:

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

Vous pouvez le rendre plus concis avec une méthode d'assistance:

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

Devient:

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

Si vous ne voulez pas réserve -1 alors vous pouvez utiliser des valeurs nulles:

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

Que se passe-t-il si '-1' est dans la liste? – NetMage

+0

@NetMage L'OP a utilisé -1 dans son exemple. – Dai

+2

Serait préférable d'utiliser 'Nullable'. – kiziu

1

l'aide d'une belle méthode d'extension

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

(que vous pouvez remplacer par new[] { n } si l'on préfère)

input.Split(',').SelectMany(s => Int32.TryParse(s, out var n) ? n.AsSingleton() : Enumerable.Empty<int>()).ToList() 
+1

C'est une solution soignée, n'aime pas vraiment l'itérateur tho. – Deadzone

+0

Vous voulez dire le 'AsSingleton'? Comme mentionné, vous pouvez le remplacer par 'new [] {n}' à la place. – NetMage

+4

Ceci est une mauvaise surcharge du nom 'Singleton'. Il y a sûrement un meilleur nom. – 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})); 
    } 
} 

Il ne retourne pas List<int> mais je dois tracer la ligne quelque part. Vous pouvez en faire une liste.

+2

pourrait être une méthode statique, donc vous n'avez pas besoin de créer une nouvelle classe –

+0

J'aime votre solution. Ma variante est: 'var a = input.Split (','). Où (str => int.TryParse (str, out nombre)). Sélectionnez (_ => nombre);' – Gioce90

0

Je préfère faire une belle fonction d'aide:

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

Ensuite, il est une question simple à analyser:

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

qui donne:

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

Si les chiffres sont toujours simples chiffres ASCII:

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

peu d'alternatives RegEx plus lentes pour le plaisir:

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

Non, ne sont pas des chiffres seulement. Mais j'aime ta première solution – Gioce90

0

Pourquoi faut-il être? LINQ

Essayez:

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

Les tests ne sont pas concluants car avec de si faibles temps de fonctionnement dans les ms tu as une grande quantité de gigue. Pour éviter ces tests en général devrait fonctionner au moins plusieurs secondes. Si vous exécutez votre violon plusieurs fois, j'ai parfois l'impression que la version LINQ est 10 fois plus rapide, ce qui montre vraiment la gigue avec la façon dont vous avez défini votre test. – ckuri

0

Voici une extension LINQ générique, qui utilise un delegate. Cela vous permettra de passer une fonction en retournant un bool, tout en "conservant" le résultat de la variable out (comme int.TryParse).


Utilisation:

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