web-dev-qa-db-fra.com

Joindre une collection d'objets dans une chaîne séparée par des virgules

Dans de nombreux endroits de notre code, nous avons des collections d'objets, à partir desquelles nous devons créer une liste séparée par des virgules. Le type de collection varie: il peut s'agir d'un DataTable pour lequel nous avons besoin d'une certaine colonne, ou d'une liste <Client>, etc.

Maintenant, nous parcourons la collection et utilisons la concaténation de chaînes, par exemple:

string text = "";
string separator = "";
foreach (DataRow row in table.Rows)
{
    text += separator + row["title"];
    separator = ", ";
}

Y at-il un meilleur modèle pour cela? Idéalement, j'aimerais une approche que nous pourrions réutiliser en envoyant simplement une fonction pour obtenir le bon champ/propriété/colonne de chaque objet.

25
Helen Toomik
// using System.Collections;
// using System.Collections.Generic;
// using System.Linq

public delegate string Indexer<T>(T obj);

public static string concatenate<T>(IEnumerable<T> collection, Indexer<T> indexer, char separator)
{
    StringBuilder sb = new StringBuilder();
    foreach (T t in collection) sb.Append(indexer(t)).Append(separator);
    return sb.Remove(sb.Length - 1, 1).ToString();
}

// version for non-generic collections
public static string concatenate<T>(IEnumerable collection, Indexer<T> indexer, char separator)
{
    StringBuilder sb = new StringBuilder();
    foreach (object t in collection) sb.Append(indexer((T)t)).Append(separator);
    return sb.Remove(sb.Length - 1, 1).ToString();
}

// example 1: simple int list
string getAllInts(IEnumerable<int> listOfInts)
{
    return concatenate<int>(listOfInts, Convert.ToString, ',');
}

// example 2: DataTable.Rows
string getTitle(DataRow row) { return row["title"].ToString(); }
string getAllTitles(DataTable table)
{
    return concatenate<DataRow>(table.Rows, getTitle, '\n');
}

// example 3: DataTable.Rows without Indexer function
string getAllTitles(DataTable table)
{
    return concatenate<DataRow>(table.Rows, r => r["title"].ToString(), '\n');
}
10
Hosam Aly
string.Join(", ", Array.ConvertAll(somelist.ToArray(), i => i.ToString()))
94
leppie
static string ToCsv<T>(IEnumerable<T> things, Func<T, string> toStringMethod)
{
    StringBuilder sb = new StringBuilder();

    foreach (T thing in things)
        sb.Append(toStringMethod(thing)).Append(',');

    return sb.ToString(0, sb.Length - 1); //remove trailing ,
}

Utilisez comme ceci:

DataTable dt = ...; //datatable with some data
Console.WriteLine(ToCsv(dt.Rows, row => row["ColName"]));

ou:

List<Customer> customers = ...; //assume Customer has a Name property
Console.WriteLine(ToCsv(customers, c => c.Name));

Je n'ai pas de compilateur à portée de main, mais en théorie cela devrait fonctionner. Et comme chacun le sait, en théorie, la pratique et la théorie sont les mêmes. En pratique, ils ne le sont pas.

11
Matt Howells

J'ai trouvé string.Join and Lambda Select> aide à écrire le code minimum.

        List<string> fruits = new List<string>();
        fruits.Add("Mango");
        fruits.Add("Banana");
        fruits.Add("Papaya");

        string commaSepFruits = string.Join(",", fruits.Select(f => "'" + f + "'"));
        Console.WriteLine(commaSepFruits);

        List<int> ids = new List<int>();
        ids.Add(1001);
        ids.Add(1002);
        ids.Add(1003);

        string commaSepIds = string.Join(",", ids);
        Console.WriteLine(commaSepIds);

        List<Customer> customers = new List<Customer>();
        customers.Add(new Customer { Id = 10001, Name = "John" });
        customers.Add(new Customer { Id = 10002, Name = "Robert" });
        customers.Add(new Customer { Id = 10002, Name = "Ryan" });

        string commaSepCustIds = string.Join(", ", customers.Select(cust => cust.Id));
        string commaSepCustNames = string.Join(", ", customers.Select(cust => "'" + cust.Name + "'"));

        Console.WriteLine(commaSepCustIds);
        Console.WriteLine(commaSepCustNames);

        Console.ReadLine();
10
Umang Patel

Vous pouvez écrire une fonction qui transforme un IEnumerable en une chaîne séparée par des virgules

public string Concat(IEnumerable<string> stringList)
{
    StringBuilder textBuilder = new StringBuilder();
    string separator = String.Empty;
    foreach(string item in stringList)
    {
        textBuilder.Append(separator);
        textBuilder.Append(item);
        separator = ", ";
    }
    return textBuilder.ToString();
}

Vous pouvez ensuite utiliser Linq pour interroger votre collection/dataset/etc afin de fournir la chaîne de caractères.

6
Mendelt

Dans .NET 4, vous pouvez simplement faire string.Join(", ", table.Rows.Select(r => r["title"]))

6
Ian Mercer

En passant: la première modification que je ferais consiste à utiliser la classe StringBuilder au lieu d'une chaîne - cela économisera des ressources.

2
Galwegian

J'adore Matt Howells réponse dans cet article: 

Je devais en faire une extension:

public static string ToCsv<T>(this IEnumerable<T> things, Func<T, string> toStringMethod)

Utilisation: [Je reçois tous les emails et les transforme en chaîne csv pour les emails]:

var list = Session.Find("from User u where u.IsActive = true").Cast<User>();

return list.ToCsv(i => i.Email);
2
BigBlondeViking
string strTest = "1,2,4,6";
string[] Nums = strTest.Split(',');
Console.Write(Nums.Aggregate<string>((first, second) => first + "," + second));
//OUTPUT:
//1,2,4,6
1

Pour les collections, vous pouvez également utiliser cette méthode, par exemple:

string.Join(", ", contactsCollection.Select(i => i.FirstName);

Vous pouvez sélectionner n'importe quelle propriété que vous souhaitez séparer.

1
Sapan Ghafuri

Voici ma réponse préférée adaptée à la question, .__ et corrigée Convert to ConvertAll:

string text = string.Join(", ", Array.ConvertAll(table.Rows.ToArray(), i => i["title"]));
0
toddmo