LINQ list to sentence format (insert commas & “and”)

后端 未结 17 1299
天命终不由人
天命终不由人 2021-01-12 23:33

I have a linq query that does something simple like:

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

I then want a function or linq lambda,

相关标签:
17条回答
  • 2021-01-13 00:22

    Just for fun, here’s something that really uses functional LINQ — no loop and no StringBuilder. Of course, it’s pretty slow.

    var list = new[] { new { ID = 1, Name = "John" },
                       new { ID = 2, Name = "Mark" },
                       new { ID = 3, Name = "George" } };
    
    var resultAggr = list
        .Select(item => item.ID + ":" + item.Name)
        .Aggregate(new { Sofar = "", Next = (string) null },
                   (agg, next) => new { Sofar = agg.Next == null ? "" :
                                                agg.Sofar == "" ? agg.Next :
                                                agg.Sofar + ", " + agg.Next,
                                        Next = next });
    var result = resultAggr.Sofar == "" ? resultAggr.Next :
                 resultAggr.Sofar + " and " + resultAggr.Next;
    
    // Prints 1:John, 2:Mark and 3:George
    Console.WriteLine(result);
    
    0 讨论(0)
  • 2021-01-13 00:24

    Y'all are making it too complicated:

    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 讨论(0)
  • 2021-01-13 00:24

    Here's a method that doesn't use LINQ, but is probably as efficient as you can get:

    public static string Join<T>(this IEnumerable<T> list,
                                 string joiner,
                                 string lastJoiner = null)
    {
        StringBuilder sb = new StringBuilder();
        string sep = null, lastItem = null;
        foreach (T item in list)
        {
            if (lastItem != null)
            {
                sb.Append(sep);
                sb.Append(lastItem);
                sep = joiner;
            }
            lastItem = item.ToString();
        }
        if (lastItem != null)
        {
            if (sep != null)
                sb.Append(lastJoiner ?? joiner);
            sb.Append(lastItem);
        }
        return sb.ToString();
    }
    
    Console.WriteLine(people.Select(x => x.ID + ":" + x.Name).Join(", ", " and "));
    

    Since it never creates a list, looks at an element twice, or appends extra stuff to the StringBuilder, I don't think you can get more efficient. It also works for 0, 1, and 2 elements in the list (as well as more, obviously).

    0 讨论(0)
  • 2021-01-13 00:25

    This is not pretty but will do the job using 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 讨论(0)
  • 2021-01-13 00:26

    Here's one using a slightly modified version of my answer to Eric Lippert's Challenge which is IMHO the most concise with easy to follow logic (if you're familiar with LINQ).

    static string CommaQuibblingMod<T>(IEnumerable<T> items)
    {
        int count = items.Count();
        var quibbled = items.Select((Item, index) => new { Item, Group = (count - index - 2) > 0})
                            .GroupBy(item => item.Group, item => item.Item)
                            .Select(g => g.Key
                                ? String.Join(", ", g)
                                : String.Join(" and ", g));
        return String.Join(", ", quibbled);  //removed braces
    }
    
    //usage
    var items = k.Select(item => String.Format("{0}:{1}", item.ID, item.Name));
    string formatted = CommaQuibblingMod(items);
    
    0 讨论(0)
  • 2021-01-13 00:26
    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);
    }
    

    could be optimized by moving k.Last() computation to before the loop

    0 讨论(0)
提交回复
热议问题