LINQ список к формату предложения (вставьте запятые & "и") - PullRequest
6 голосов
/ 29 сентября 2010

У меня есть запрос linq, который делает что-то простое:

var k = people.Select(x=>new{x.ID, x.Name});

Затем я хочу функцию или лямбда-строку linq, или что-то, что выводит имена в формате предложения, используя запятые и "ands".

{1, John}
{2, Mark}
{3, George}

до

"1:John, 2:Mark and 3:George"

Я в порядке с жестким кодированием части ID + ":" + Name, но это может быть ToString () в зависимости от типа результата запроса linq.Мне просто интересно, есть ли удобный способ сделать это с помощью linq или String.Format ().

Ответы [ 17 ]

0 голосов
/ 29 сентября 2010

Подход StringBuilder

Вот Aggregate с StringBuilder.Есть некоторые определения положения, которые сделаны, чтобы очистить строку и вставить «и», но все это делается на уровне StringBuilder.

var people = new[]
{
    new { Id = 1, Name = "John" },
    new { Id = 2, Name = "Mark" },
    new { Id = 3, Name = "George" }
};

var sb = people.Aggregate(new StringBuilder(),
             (s, p) => s.AppendFormat("{0}:{1}, ", p.Id, p.Name));
sb.Remove(sb.Length - 2, 2); // remove the trailing comma and space

var last = people.Last();
// index to last comma (-2 accounts for ":" and space prior to last name)
int indexComma = sb.Length - last.Id.ToString().Length - last.Name.Length - 2;

sb.Remove(indexComma - 1, 1); // remove last comma between last 2 names
sb.Insert(indexComma, "and ");

// 1:John, 2:Mark and 3:George
Console.WriteLine(sb.ToString());

Подход String.Join можно было бы использовать вместо этого, ноВставка "и" и удаление запятой приведут к генерации ~ 2 новых строк.


Подход Regex

Вот еще один подход с использованием регулярных выражений, который вполне понятен (ничего слишком загадочного).

var people = new[]
{
    new { Id = 1, Name = "John" },
    new { Id = 2, Name = "Mark" },
    new { Id = 3, Name = "George" }
};
var joined = String.Join(", ", people.Select(p => p.Id + ":" + p.Name).ToArray());
Regex rx = new Regex(", ", RegexOptions.RightToLeft);
string result = rx.Replace(joined, " and ", 1); // make 1 replacement only
Console.WriteLine(result);

Шаблон просто ", ".Магия заключается в RegexOptions.RightToLeft, который заставляет совпадение происходить справа и тем самым заставляет замену происходить при последнем появлении запятой.Не существует статического метода Regex, который принимает число замен на RegexOptions, следовательно, используется экземпляр.

0 голосов
/ 29 сентября 2010

Вы все усложняете:

var list = k.Select(x => x.ID + ":" + x.Name).ToList();
var str = list.LastOrDefault();
str = (list.Count >= 2 ? list[list.Count - 2] + " and " : null) + str;
str = string.Join(", ", list.Take(list.Count - 2).Concat(new[]{str}));
0 голосов
/ 29 сентября 2010

Это не красиво, но будет делать работу, используя LINQ

string s = string.Join(",", k.TakeWhile(X => X != k.Last()).Select(X => X.Id + ":" + X.Name).ToArray()).TrimEnd(",".ToCharArray()) + " And " + k.Last().Id + ":" + k.Last().Name;
0 голосов
/ 29 сентября 2010

Улучшение (надеюсь) ответа KeithS:

string nextBit = "";
var sb = new StringBuilder();
foreach(Person person in list)
{
    sb.Append(nextBit);
    sb.Append(", ");
    nextBit = String.Format("{0}:{1}", person.ID, person.Name);
}
sb.Remove(sb.Length - 3, 2);
sb.Append(" and ");
sb.Append(nextBit);
0 голосов
/ 04 октября 2010
static public void Linq1()
{
    var k = new[] { new[] { "1", "John" }, new[] { "2", "Mark" }, new[] { "3", "George" } };

    Func<string[], string> showPerson = p => p[0] + ": " + p[1];

    var res = k.Skip(1).Aggregate(new StringBuilder(showPerson(k.First())),
        (acc, next) => acc.Append(next == k.Last() ? " and " : ", ").Append(showPerson(next)));

    Console.WriteLine(res);
}

можно оптимизировать, переместив вычисления k.Last () до цикла

0 голосов
/ 25 июля 2016
    public static string ToListingCommaFormat(this List<string> stringList)
    {
        switch(stringList.Count)
        {
            case 0:
                return "";
            case 1:
                return stringList[0];
            case 2:
                return stringList[0] + " and " + stringList[1];
            default:
                return String.Join(", ", stringList.GetRange(0, stringList.Count-1)) 
                    + ", and " + stringList[stringList.Count - 1];
        }
    }

Этот метод работает быстрее, чем «эффективный» метод Join, опубликованный Gabe. Для одного и двух элементов это во много раз быстрее, а для 5-6 строк - примерно на 10% быстрее. Здесь нет зависимости от LINQ. String.Join быстрее, чем StringBuilder для небольших массивов, которые типичны для читабельного текста. В грамматике они называются перечислением запятых , и последняя запятая всегда должна быть включена, чтобы избежать двусмысленности. Вот полученный код:

people.Select(x=> x.ID.ToString() + ":" + x.Name).ToList().ToListingCommaFormat();

0 голосов
/ 29 сентября 2010

Это может быть способ, которым вы можете достичь своей цели

var list = new[] { new { ID = 1, Name = "John" }, 
                   new { ID = 2, Name = "Mark" }, 
                   new { ID = 3, Name = "George" }
                 }.ToList();

int i = 0;

string str = string.Empty;

var k = list.Select(x => x.ID.ToString() + ":" + x.Name + ", ").ToList();

k.ForEach(a => { if (i < k.Count() - 1) { str = str +  a; } else { str = str.Substring(0, str.Length -2) + " and " + a.Replace("," , ""); } i++; });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...