#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, просто мысль