2010-09-03 6 views
10

¿Cuál es la forma más eficiente de convertir un diccionario a una cadena formateada?Diccionario más eficiente <K,V> .ToString() con formato?

ej .:

Mi método:

public string DictToString(Dictionary<string, string> items, string format){ 

    format = String.IsNullOrEmpty(format) ? "{0}='{1}' " : format; 

    string itemString = ""; 
    foreach(var item in items){ 
     itemString = itemString + String.Format(format,item.Key,item.Value); 
    } 

    return itemString; 
} 

¿Hay una mejor manera/más concisa/más eficiente?

Nota: el Diccionario tendrá como máximo 10 elementos y no estoy comprometido a utilizar si existe otro "par clave-valor" Tipo de objeto similar

También, ya que estoy volviendo cadenas de todos modos , ¿cómo se vería una versión genérica?

+1

Creo que es bastante eficiente; simplemente cámbialo para utilizar un 'StringBuilder' en lugar de cadena de concatenación y deberías establecerlo. – Gabe

Respuesta

21

acabo reescribió la versión a ser un poco más genérico y utilizar StringBuilder:

public string DictToString<T, V>(IEnumerable<KeyValuePair<T, V>> items, string format) 
{ 
    format = String.IsNullOrEmpty(format) ? "{0}='{1}' " : format; 

    StringBuilder itemString = new StringBuilder(); 
    foreach(var item in items) 
     itemString.AppendFormat(format, item.Key, item.Value); 

    return itemString.ToString(); 
} 
+0

Esto no funcionará como piensas: 'IDictionary ' no es compatible con 'IDictionary '; sin embargo, puedes hacer el valor genérico. – Lee

+0

Lee: ¿Qué tal ahora? – Gabe

+0

Esto funcionará sin problemas – abatishchev

16
public string DictToString<TKey, TValue>(Dictionary<TKey, TValue> items, string format) 
{ 
    format = String.IsNullOrEmpty(format) ? "{0}='{1}' " : format; 
    return items.Aggregate(new StringBuilder(), (sb, kvp) => sb.AppendFormat(format, kvp.Key, kvp.Value)).ToString(); 
} 
+0

Creo que puedes usar 'IDictionary' – abatishchev

0

Gabe, si va a ser genérica, ser genéricos:

public string DictToString<T>(IDictionary<string, T> items, string format) 
{ 
    format = String.IsNullOrEmpty(format) ? "{0}='{1}' " : format; 

    StringBuilder itemString = new StringBuilder(); 
    foreach(var item in items) 
     itemString.AppendFormat(format, item.Key, item.Value); 

    return itemString.ToString(); 
} 
+0

Originalmente quise decir genérico en el sentido" uses IDictionary en vez de Dictionary ", pero ya estaba haciendo lo que sugeriste cuando publicaste el tuyo. – Gabe

+0

@Gabe: El problema es que no creo que pueda pasar un 'Diccionario ' como un 'IDiccionario ' –

+0

Tiene razón; Acabo de publicar antes de que terminara de escribir la maldita cosa. – Gabe

1

I piense que la eficiencia no es una preocupación con solo 10 cuerdas, pero tal vez no quiera confiar en que solo sea diez.

Concatenación de cadenas crea un nuevo objeto String en la memoria, ya que los objetos String son inmutables. Esto también sugiere que otras operaciones de Cadena pueden crear nuevas instancias, como reemplazar. Por lo general, esto se evita mediante el uso de StringBuilder.

StringBuilder lo evita mediante el uso de un búfer en el que opera; cuando el valor de StringBuilder se concatena con otra cadena, los contenidos se agregan al final del búfer.

Sin embargo hay advertencias, ver this paragraph:

Consideraciones sobre el rendimiento

[...]

La realización de una operación de concatenación de una cadena o objeto StringBuilder depende en cómo a menudo ocurre una asignación de memoria. Una operación de concatenación cadena siempre asigna memoria, mientras que una operación de concatenación StringBuilder solamente asigna memoria si la memoria intermedia de objetos StringBuilder es demasiado pequeña para acomodar los nuevos datos. En consecuencia, la clase String es preferible para una operación de concatenación si un número fijo de cadena objetos son concatenados. En ese caso , las operaciones de concatenación individual incluso pueden combinarse en una sola operación por el compilador.Un objeto StringBuilder es preferible para una operación de concatenación si un número arbitrario de cadenas son concatenan; por ejemplo, si un bucle concatena un número aleatorio de cadenas de entrada de usuario.

lo tanto un caso (artificial) como esto probablemente no debería ser reemplazado con StringBuilder:

string addressLine0 = Person.Street.Name + " " + Person.Street.Number + " Floor " + Person.Street.Floor; 

... como el compilador podría ser capaz de reducir esto a una forma más eficiente. También es muy debatible si sería lo suficientemente ineficiente como para importar en el mayor esquema de cosas.

Siguiendo las recomendaciones de Microsoft es probable que desee utilizar StringBuilder en lugar

+1

Nitpick: El hecho de que la concatenación de cadenas utilizando el operador '+' crea nuevos objetos 'string' en la memoria no guarda relación con el hecho de que el tipo' string' es inmutable (aunque ambos son verdaderos). –

+0

@Dan Tao: Estoy un poco grueso en este momento, ¿podrías explicar por qué? Parece que las operaciones String + String obtienen un tratamiento especial del compilador, ¿tiene esto algo que ver? – Skurmedel

+0

@Skurmedal: Entonces, el operador '+' es estático, toma dos argumentos y devuelve un nuevo valor. Podrías pensarlo, realmente, como un método estático. Ahora bien, el hecho de que un método acepte dos argumentos y devuelva un nuevo valor no implica que el tipo de argumentos sea inmutable. Pude 'Concat' dos objetos' List 'juntos para obtener un nuevo' IEnumerable 'sin afectar cualquiera de 'List '; esto no sería porque 'List ' es inmutable (no lo es). Simplemente sería la forma en que funciona 'Concat'. Por lo tanto, aunque 'string' sea inmutable, podría ser igual de * not * be - en lo que respecta a la concatenación. –

9

Este método

public static string ToFormattedString<TKey, TValue>(this IDictionary<TKey, TValue> dic, string format, string separator) 
{ 
    return String.Join(
     !String.IsNullOrEmpty(separator) ? separator : " ", 
     dic.Select(p => String.Format(
      !String.IsNullOrEmpty(format) ? format : "{0}='{1}'", 
      p.Key, p.Value))); 
} 

utilizado manera siguiente (al igual que las otras respuestas altamente adecuados muestran.):

dic.ToFormattedString(null, null); // default format and separator 

convertirá

new Dictionary<string, string> 
{ 
    { "a", "1" }, 
    { "b", "2" } 
}; 

a

a='1' b='2' 

o

dic.ToFormattedString("{0}={1}", ", ") 

a

a=1, b=2 

No se olvide de una sobrecarga:

public static string ToFormattedString<TKey, TValue>(this IDictionary<TKey, TValue> dic) 
{ 
    return dic.ToFormattedString(null, null); 
} 

Usted puede utilizar genérica TKey/TValue porque cualquier objeto tiene ToString() que será utilizado por String.Format().

Y tan lejos como IDictionary<TKey, TValue> es IEnumerable<KeyValuePair<TKey, TValue>> puede usar cualquiera. Prefiero IDictionary para más código de expresividad.

2

Levemente versión mejorada de las otras respuestas, utilizando métodos de extensión y los parámetros por defecto, y también envolver pares clave/valor en {}:

public static string ItemsToString<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> items, string format = "{0}='{1}' ") 
{ 
    return items 
     .Aggregate(new StringBuilder("{"), (sb, kvp) => sb.AppendFormat(format, kvp.Key, kvp.Value)) 
     .Append('}') 
     .ToString(); 
} 

El método puede entonces ser llamados directamente desde el diccionario/enumerable:

string s = myDict.ItemsToString() 
+0

Esta es la solución más limpia. –

3

diccionario de formato en una línea con LINQ y string.join() (C# 6.0):

Dictionary<string, string> dictionary = new Dictionary<string, string>() 
{ 
    ["key1"] = "value1", 
    ["key2"] = "value2"    
}; 

string formatted = string.Join(", ", dictionary.Select(kv => $"{kv.Key}={kv.Value}")); // key1=value1, key2=value2 

Puede crear un método de extensión simple como este:

public static class DictionaryExtensions 
{ 
    public static string ToFormatString<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, string format = null) 
    { 
     format = string.IsNullOrEmpty(format) ? "{0}='{1}'" : format; 
     return string.Join(", ", dictionary.Select(kv => string.Format(format, kv.Key, kv.Value))); 
    } 
} 
Cuestiones relacionadas