Как извлечь элементы в указанных местах из списка строк, разделенных запятыми, с помощью LINQ

#c# #linq #csv #parsing

#c# #linq #csv #синтаксический анализ

Вопрос:

У меня есть список строк, разделенных запятыми, и мне нужно извлечь 1-й и 3-й элементы из всех строк.

 List<string> list = new List<string>()
{
    "1,2,3",
    "4,5,6",
    "7,8,9",
    "10,11,12"
};
List<Tuple<string, string>> parsed = new List<Tuple<string, string>>(list.Count);
foreach (string s in list)
{
    string[] items = s.Split(',');
    parsed.Add(new Tuple<string, string>(items[0], items[2]));
}
Console.WriteLine(string.Join(Environment.NewLine, parsed.Select(p => p.Item1  ","  p.Item2)));
Console.ReadLine();
  

Это приводит:

1,3
4,6
7,9
10,12

Но когда я пытаюсь написать это с помощью LINQ, я не могу получить что-то более простое, чем:

 IEnumerable<Tuple<string, string>> parsed = list.Select(
    s =>
    {
        string[] items = s.Split(',');
        return new Tuple<string, string>(items[0], items[2]);
    });
  

Мне было интересно, возможно ли избавиться от этого {} блока и заменить его вызовами функций LINQ. Чтобы было ясно, я задаю этот вопрос только для того, чтобы расширить свои знания о функциях и возможностях LINQ, поэтому любое предложение приветствуется.

Редактировать:

Пока что все предлагаемые коды вызывают split функцию дважды. Есть ли способ получить желаемый результат, просто вызвав его один раз? Что-то вроде:

 var parsed = list.Select(s => s.Split(',').Magic(...));
  

Кроме того, в приведенном выше примере кода я не имел в виду первые и последние элементы. Я действительно имею в виду элементы в указанных местах.

Комментарии:

1. string[] items = s.Split(','); — что-то вроде этого или подобное new Tuple<string, string>(items.Split(',')[0], items.Split(',')[2]); ?

2. Вы действительно хотите, чтобы вывод был в Tuple ?

3. @MaciejLos, ну, я действительно думал о чем-то, что не требует дополнительных split .

4. @SowmyadharGourishetty, вернемся сюда, что я должен сделать new MyClass(items[0], items[3], items[12], ...) !

5. @saastn, изменил ответ, чтобы избежать множественного Split

Ответ №1:

Если вы работаете с C # 7 или более поздней версией, то вы можете написать еще более простым способом,

 IEnumerable<Tuple<string, string>> parsed = list.Select(
    s => (s.Split(',')[0], s.Split(',')[2]));
  

Ответ №2:

Вы можете сделать что-то вроде приведенного ниже

 IEnumerable<Tuple<string, string>> parsed = list.Select(
                s =>
                {
                    var spl = s.Split(',');
                    return new Tuple<string, string>(spl[0], spl[2]);
                    // return new MyClass(spl[0], spl[2], ... ,spl[n]);
                });
  

Если вы хотите вернуть , разделенный список обратно, удалив среднее число, вы можете использовать Regex для его замены.

 IEnumerable<string> afterUpdate = list.Select(s => Regex.Replace(s, @",[0-9]*,", ","));
  

Вывод для этого будет

 {
    "1,3",
    "4,6",
    "7,9",
    "10,12"
};
  

Ответ №3:

Может быть, это могло бы помочь…

         //----------------Linq.----------------------
        
        //Data Source 
        var source = new List<string> { "1,2,3", "4,5,6", "7,8,9", "10,11,12" };
        //var sourceTest = new List<string> { "11,45,6,5,", "2,3,4,5,6", "1,7,40,30", "10,20,30,40,50" };
        //var sourceTest2 = new List<string> { "15,12,11,45,6,5,", "1,2,3,4,5,6", "1,7,9,40,30", "60,20,70,80,90,100" };
        //Query Creation
        var queryLambda = source.Select(item => new
                                    {
                                        FirstItem = item.Split(',').FirstOrDefault(),
                                        ThirdItem = item.Split(',').Skip(2).FirstOrDefault()
                                    }).ToList();
        var query = (from items in source
                     select new
                     {
                         FirstItem = items.Split(',').FirstOrDefault(),
                         ThirdItem = items.Split(',').Skip(2).FirstOrDefault()
                     }).ToList();

        //Query Execution
        queryLambda.ForEach(item => { Console.WriteLine(string.Join(",", new string[] { item.FirstItem, item.ThirdItem })); });
        Console.WriteLine();
        query.ForEach(item => { Console.WriteLine(string.Join(",", new string[] { item.FirstItem, item.ThirdItem })); });
        Console.ReadLine();
  

Комментарии:

1. Как вы думаете, почему вызов FirstOrDefault() и Skip() лучше, чем прямое индексирование?

2. Это безопасный способ, индексация может быть быстрее в небольшом наборе, но при этом вам не нужно заботиться о длине источника, при индексировании вы должны проверить, существует ли index[], но FirstOrDefault() безопасно возвращает вам null, просто мысль