¿Cómo unir int [] a una cadena separada por caracteres en .NET?

101

Tengo una matriz de enteros:

int[] number = new int[] { 2,3,6,7 };

¿Cuál es la forma más fácil de convertirlos en una sola cadena donde los números están separados por un carácter (como: "2,3,6,7" ?

Estoy en C # y .NET 3.5.

Riri
fuente
3
¡TAN rocas! ¡Obtuve estas 3 excelentes respuestas en 10 minutos un domingo!
Riri
4
A partir de .NET 4.0ahí, existen métodos que toman una matriz de objetos y un IEnumerable para que pueda hacerlo string.join(",", number). Sé que la pregunta especifica .NET 3.5, así que no hice de esto una respuesta, pero aparece en búsquedas que no especifican una versión y saber que es posible en 4.0 podría ayudar a alguien.
Jason Goemaat

Respuestas:

162
var ints = new int[] {1, 2, 3, 4, 5};
var result = string.Join(",", ints.Select(x => x.ToString()).ToArray());
Console.WriteLine(result); // prints "1,2,3,4,5"

EDITAR : A partir de (al menos) .NET 4.5,

var result = string.Join(",", ints.Select(x => x.ToString()).ToArray());

es equivalente a:

var result = string.Join(",", ints);

EDITAR :

Veo varias soluciones que anuncian el uso de StringBuilder. Alguien se queja de que el método Join debería tomar un argumento IEnumerable.

Te voy a decepcionar :) String.Join requiere una matriz por una única razón: rendimiento. El método de unión necesita conocer el tamaño de los datos para preasignar eficazmente la cantidad de memoria necesaria.

Aquí hay una parte de la implementación interna del método String.Join:

// length computed from length of items in input array and length of separator
string str = FastAllocateString(length);
fixed (char* chRef = &str.m_firstChar) // note than we use direct memory access here
{
    UnSafeCharBuffer buffer = new UnSafeCharBuffer(chRef, length);
    buffer.AppendString(value[startIndex]);
    for (int j = startIndex + 1; j <= num2; j++)
    {
        buffer.AppendString(separator);
        buffer.AppendString(value[j]);
    }
}

Soy demasiado vago para comparar el rendimiento de los métodos sugeridos. Pero algo me dice que Join ganará :)

aku
fuente
Esta es probablemente la mejor apuesta usando métodos de extensión .NET centrales, pero realmente deseaba que string.Join () aceptara un IEnumerable <string> para evitar la conversión ToArray ().
spoulson
Nada evita que alguien sobrecargue la cadena. Únase para tomar un IEnumerable, también. ;)
Robert P
1
Esta es probablemente también la solución más fácil, y no solo la más rápida.
Dave Van den Eynde
9
.NET 4 proporciona una sobrecarga String.Join que acepta IEnumerable como parámetro. msdn.microsoft.com/en-us/library/dd783876.aspx
Ryan Kohn
using System.Linq;es requerido.
Gayan Weerakutti
32

Aunque el OP especificó .NET 3.5, las personas que quieran hacer esto en .NET 2.0 con C # 2 pueden hacer esto:

string.Join(",", Array.ConvertAll<int, String>(ints, Convert.ToString));

Encuentro que hay otros casos en los que el uso de las funciones Convert.xxx es una alternativa más ordenada a una lambda, aunque en C # 3 la lambda podría ayudar a la inferencia de tipos.

Una versión bastante compacta de C # 3 que funciona con .NET 2.0 es la siguiente:

string.Join(",", Array.ConvertAll(ints, item => item.ToString()))
Will Dean
fuente
11

Una mezcla de los dos enfoques sería escribir un método de extensión en IEnumerable <T> que usa un StringBuilder. Aquí hay un ejemplo, con diferentes sobrecargas dependiendo de si desea especificar la transformación o simplemente confiar en ToString simple. He nombrado el método "JoinStrings" en lugar de "Join" para evitar confusiones con el otro tipo de Join. Quizás alguien pueda pensar en un nombre mejor :)

using System;
using System.Collections.Generic;
using System.Text;

public static class Extensions
{
    public static string JoinStrings<T>(this IEnumerable<T> source, 
                                        Func<T, string> projection, string separator)
    {
        StringBuilder builder = new StringBuilder();
        bool first = true;
        foreach (T element in source)
        {
            if (first)
            {
                first = false;
            }
            else
            {
                builder.Append(separator);
            }
            builder.Append(projection(element));
        }
        return builder.ToString();
    }

    public static string JoinStrings<T>(this IEnumerable<T> source, string separator)
    {
        return JoinStrings(source, t => t.ToString(), separator);
    }
}

class Test
{

    public static void Main()
    {
        int[] x = {1, 2, 3, 4, 5, 10, 11};

        Console.WriteLine(x.JoinStrings(";"));
        Console.WriteLine(x.JoinStrings(i => i.ToString("X"), ","));
    }
}
Jon Skeet
fuente
¡Buena solución! Sin embargo, no necesita el parámetro de proyección, solo puede escribir x.Select (i => i.ToString ("X")). JoinStrings (";") que es más idiomático.
JacquesB
Sí, pensé en eso después. De vez en cuando es bueno poder especificarlo todo de una vez, pero definitivamente es más elegante dividir las operaciones :)
Jon Skeet
8
String.Join(";", number.Select(item => item.ToString()).ToArray());

Tenemos que convertir cada uno de los elementos a Stringantes de poder unirlos, por lo que tiene sentido usar Selectuna expresión lambda. Esto es equivalente a mapen algunos otros idiomas. Luego tenemos que convertir la colección resultante de cadenas de nuevo en una matriz, porque String.Joinsolo acepta una matriz de cadenas.

El ToArray()es un poco feo que pienso. String.Joinrealmente debería aceptar IEnumerable<String>, no hay razón para restringirlo solo a matrices. Esto probablemente se deba a que Joines anterior a los genéricos, cuando las matrices eran el único tipo de colección con tipo disponible.

JacquesB
fuente
5

Si su matriz de números enteros puede ser grande, obtendrá un mejor rendimiento con StringBuilder. P.ej:

StringBuilder builder = new StringBuilder();
char separator = ',';
foreach(int value in integerArray)
{
    if (builder.Length > 0) builder.Append(separator);
    builder.Append(value);
}
string result = builder.ToString();

Editar: Cuando publiqué esto, tenía la impresión errónea de que "StringBuilder.Append (int value)" logró agregar internamente la representación de cadena del valor entero sin crear un objeto de cadena. Esto es incorrecto: inspeccionar el método con Reflector muestra que simplemente agrega value.ToString ().

Por lo tanto, la única diferencia de rendimiento potencial es que esta técnica evita la creación de una matriz y libera las cadenas para la recolección de basura un poco antes. En la práctica, esto no hará ninguna diferencia medible, así que voté a favor de esta mejor solución .

Joe
fuente
¿Lo has medido para que sea más rápido? String.Join también usa un StringBuilder.
JacquesB
Sí, pero primero debe convertir todo en una matriz, lo que está lejos de ser ideal. En particular, significa que debe tener todas las cadenas convertidas en la memoria al mismo tiempo, antes de comenzar a construir la cadena resultante.
Jon Skeet
OTOH String.Join precalcula el tamaño del búfer StringBuilder para evitar el cambio de tamaño. Por lo tanto, podría ser más rápido incluso si requiere más memoria.
JacquesB
5

La pregunta es cuál es la "forma más sencilla de convertirlos en una sola cadena donde los números están separados por un carácter".

La forma más sencilla es:

int[] numbers = new int[] { 2,3,6,7 };
string number_string = string.Join(",", numbers);
// do whatever you want with your exciting new number string

EDITAR: Esto solo funciona en .NET 4.0+, me perdí el requisito de .NET 3.5 la primera vez que leí la pregunta.

WebMasterP
fuente
Esto no es válido como cadena. El método Join solo toma una matriz de cadenas. Eche un vistazo aquí msdn.microsoft.com/en-us/library/tk0xe5h0.aspx
ppolyzos
1
Es un método sobrecargado: msdn.microsoft.com/en-us/library/dd988350 Acabo de copiar el código que escribí en una nueva aplicación de consola, agregué una Console.WriteLine y esta es la salida: 2,3,6,7
WebMasterP
1
Creo que esto está disponible solo en .net 4
Govind Malviya
Requiere una matriz de objetos (o matriz de cadenas), no una matriz int. Dará un error de "la llamada es ambigua".
LarryBud
2

Estoy de acuerdo con la expresión lambda por legibilidad y mantenibilidad, pero no siempre será la mejor opción. La desventaja de usar los enfoques IEnumerable / ToArray y StringBuilder es que tienen que hacer crecer dinámicamente una lista, ya sea de elementos o caracteres, ya que no saben cuánto espacio se necesitará para la cadena final.

Si se da el raro caso de que la velocidad sea más importante que la concisión, lo siguiente es más eficiente.

int[] number = new int[] { 1, 2, 3, 4, 5 };
string[] strings = new string[number.Length];
for (int i = 0; i < number.Length; i++)
  strings[i] = number[i].ToString();
string result = string.Join(",", strings);
DocMax
fuente
2
ints.Aggregate("", ( str, n ) => str +","+ n ).Substring(1);

También pensé que había una forma más sencilla. No sé sobre rendimiento, ¿alguien tiene alguna idea (teórica)?

vacío
fuente
Esta solución le daría ", 1,2,3,4,5".
Sarín
Gracias, agregué Substring(1)para arreglar eso (era de memoria).
anulado el
2

En .NET 4.0, la combinación de cadenas tiene una sobrecarga para params object[], por lo que es tan simple como:

int[] ids = new int[] { 1, 2, 3 };
string.Join(",", ids);

ejemplo

int[] ids = new int[] { 1, 2, 3 };
System.Data.Common.DbCommand cmd = new System.Data.SqlClient.SqlCommand("SELECT * FROM some_table WHERE id_column IN (@bla)");
cmd.CommandText = cmd.CommandText.Replace("@bla",  string.Join(",", ids));

En .NET 2.0, es un poquito más difícil, ya que no existe tal sobrecarga. Entonces necesitas tu propio método genérico:

public static string JoinArray<T>(string separator, T[] inputTypeArray)
{
    string strRetValue = null;
    System.Collections.Generic.List<string> ls = new System.Collections.Generic.List<string>();

    for (int i = 0; i < inputTypeArray.Length; ++i)
    {
        string str = System.Convert.ToString(inputTypeArray[i], System.Globalization.CultureInfo.InvariantCulture);

        if (!string.IsNullOrEmpty(str))
        { 
            // SQL-Escape
            // if (typeof(T) == typeof(string))
            //    str = str.Replace("'", "''");

            ls.Add(str);
        } // End if (!string.IsNullOrEmpty(str))

    } // Next i 

    strRetValue= string.Join(separator, ls.ToArray());
    ls.Clear();
    ls = null;

    return strRetValue;
}

En .NET 3.5, puede utilizar métodos de extensión:

public static class ArrayEx
{

    public static string JoinArray<T>(this T[] inputTypeArray, string separator)
    {
        string strRetValue = null;
        System.Collections.Generic.List<string> ls = new System.Collections.Generic.List<string>();

        for (int i = 0; i < inputTypeArray.Length; ++i)
        {
            string str = System.Convert.ToString(inputTypeArray[i], System.Globalization.CultureInfo.InvariantCulture);

            if (!string.IsNullOrEmpty(str))
            { 
                // SQL-Escape
                // if (typeof(T) == typeof(string))
                //    str = str.Replace("'", "''");

                ls.Add(str);
            } // End if (!string.IsNullOrEmpty(str))

        } // Next i 

        strRetValue= string.Join(separator, ls.ToArray());
        ls.Clear();
        ls = null;

        return strRetValue;
    }

}

Entonces puede usar el método de extensión JoinArray.

int[] ids = new int[] { 1, 2, 3 };
string strIdList = ids.JoinArray(",");

También puede usar ese método de extensión en .NET 2.0, si agrega ExtensionAttribute a su código:

// you need this once (only), and it must be in this namespace
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
    public sealed class ExtensionAttribute : Attribute {}
}
Stefan Steiger
fuente