2009-03-24 9 views
5

Aquí hay un problema simple. Tengo una aplicación que toma un número de teléfono como "13335557777", y necesita invertirlo e insertar un punto entre cada número, como este:C# string problema de manipulación

"7.7.7.7.5.5.5.3.3.3.1."

Sé que puedo hacer esto con un StringBuilder y un bucle for para invertir la cadena e insertar los puntos, pero ¿hay alguna manera inteligente de hacer esto en LINQ (o de alguna otra manera)?

Nota: para esto, no estoy realmente preocupado por el rendimiento o la asignación de memoria o lo que sea, solo curiosidad por ver cómo se haría esto en LINQ.

+0

¿De verdad quería un punto después del último 1? Si es así, cambia ligeramente el problema. –

+0

Sí, eso no estaba claro. Realmente no importa, tomaré soluciones que lo hagan de cualquier manera. –

Respuesta

8

probar este

var source = GetTheString(); 
var reversed = source.Reverse().Select(x => x.ToString()).Aggregate((x,y) => x + "." + y); 

EDITAR

Esta solución es, sin duda destinados al segmento "inteligente". Es mucho más eficiente usar un StringBuilder para construir la cadena. Esta solución crea muchas cadenas intermedias.

Edit2

Hubo cierto debate acerca de la velocidad relativa de la solución "inteligente" frente al enfoque StringBuilder. Escribí un punto de referencia rápido para medir el enfoque. Como era de esperar, StringBuilder es más rápido.

  • normal agregada (100 elementos): 00: 00: 00,0418640
  • WithStringBuilder (100 elementos): 00: 00: 00,0040099
  • normal agregada (1000 elementos): 00: 00: 00,3062040
  • WithStringBuilder (1000 elementos): 00: 00: 00,0405955
  • normal agregada (10000 elementos): 00: 00: 03,0270392
  • WithStringBuilder (10000 elementos): 00: 00: 00,4149977

Sin embargo, independientemente de si la diferencia de velocidad es significativa o no depende en gran medida de dónde se utiliza realmente en la aplicación.

Código para el índice de referencia.

public static class AggregateUnchanged { 
    public static string Run(string input) { 
     return input 
      .Reverse() 
      .Select(x => x.ToString()) 
      .Aggregate((x, y) => x + "." + y); 
    } 
} 

public static class WithStringBuilder { 
    public static string Run(string input) { 
     var builder = new StringBuilder(); 
     foreach (var cur in input.Reverse()) { 
      builder.Append(cur); 
      builder.Append('.'); 
     } 

     if (builder.Length > 0) { 
      builder.Length = builder.Length - 1; 
     } 

     return builder.ToString(); 
    } 
} 

class Program { 
    public static void RunAndPrint(string name, List<string> inputs, Func<string, string> worker) { 

     // Test case. JIT the code and verify it actually works 
     var test = worker("123456"); 
     if (test != "6.5.4.3.2.1") { 
      throw new InvalidOperationException("Bad algorithm"); 
     } 

     var watch = new Stopwatch(); 
     watch.Start(); 
     foreach (var cur in inputs) { 
      var result = worker(cur); 
     } 
     watch.Stop(); 
     Console.WriteLine("{0} ({2} elements): {1}", name, watch.Elapsed, inputs.Count); 
    } 

    public static string NextInput(Random r) { 
     var len = r.Next(1, 1000); 
     var builder = new StringBuilder(); 
     for (int i = 0; i < len; i++) { 
      builder.Append(r.Next(0, 9)); 
     } 
     return builder.ToString(); 
    } 

    public static void RunAll(List<string> input) { 
     RunAndPrint("Normal Aggregate", input, AggregateUnchanged.Run); 
     RunAndPrint("WithStringBuilder", input, WithStringBuilder.Run); 
    } 

    static void Main(string[] args) { 
     var random = new Random((int)DateTime.Now.Ticks); 
     RunAll(Enumerable.Range(0, 100).Select(_ => NextInput(random)).ToList()); 
     RunAll(Enumerable.Range(0, 1000).Select(_ => NextInput(random)).ToList()); 
     RunAll(Enumerable.Range(0, 10000).Select(_ => NextInput(random)).ToList()); 
    } 
} 
+0

Para información, aunque sea "inteligente" y "FP", no estoy seguro de que sea un buen consejo ... el número de cadenas intermedias significa que 'StringBuilder' sería más pragmático. –

+0

@Marc, de acuerdo, pidieron ingenio, así que me sentí obligado :) – JaredPar

+0

Sí, no hay problema, pedí inteligente a expensas de cualquier otra cosa. –

0

(eliminado una respuesta utilizando una lista char)

De acuerdo con el comentario sobre el otro post, mi opinión es que mientras que el LINQ, etc puede ser "inteligente", que no es necesariamente eficiente. Hará que se recopilen muchas cadenas intermedias, por ejemplo.

Me quedaría con StringBuilder etc., a menos que tenga un buen motivo para cambiarlo.

+0

¿Por qué usar un StringBuilder cuando hay String.Join específicamente para este propósito? –

+0

@Grauenwolf - porque string.Join funciona en una cadena [], no en un char [], por lo que primero deberías crear una carga de cadenas. Con StringBuilder no necesitas hacer eso. –

1
 string x = "123456"; 
     StringBuilder y = new StringBuilder(x.Length * 2); 

     for (int i = x.Length - 1; i >= 0; i--) 
     { 
      y.Append(x[i]); 
      y.Append("."); 
     } 
+0

"Sé que puedo hacer esto con un StringBuilder y un bucle for para invertir la cadena e insertar los puntos, pero ¿hay alguna manera inteligente de hacer esto en LINQ (o de alguna otra manera)?" –

+0

Solo le estaba dando el código para que no tuviera que escribirlo, imbécil – Nick

+0

Estoy contigo, Nick. +1 –

-1

Mientras esté pasando por una matriz, sería más fácil usar una cadena.Ingreso:

string[] source = GetTheStringAsArray(); 
string reversed = string.Join(".", source.Reverse()); 
+0

Se saltó el primer paso, rompiendo la cadena en una matriz o lista. –

+0

string content = "35557777"; es una matriz :) –

+0

Dime que no votaste mi respuesta y luego envias la tuya usando esencialmente la misma función ... –

0
string aString = "13335557777"; 
string reversed = (from c in aString.Reverse() 
       select c + ".").Aggregate((a, b) => a + b); 
0

Lo que tienes es un problema LINQ? Sólo el trabajo hacia atrás en un bucle por personaje:

string s = ""; 
string content = "35557777"; 
for (int i = content.Length -1; i > 0; i--) 
{ 
    s += content[i] + "."; 
} 
Console.WriteLine(s); 
Console.ReadLine(); 

Si se trata de una cadena de más de 4k, utilizar un StringBuilder. Usando LINQ para "7.7.7.7.5.5.5.3.3.3.1." no es para lo que es LINQ, márcame a -99 si quieres, y a Marc también.

+0

Gracias por la respuesta. Sé que hacer manipulaciones de cadena de caracteres no es realmente un problema de LINQ, pero esto era más una pregunta tipo "algoritmo". Tal vez hubiera sido mejor si hubiera usado una lista de objetos en lugar de una cadena. –

2

El beneficio de esto es que String.Join va a ser más barato que ".Agregar ((x, y) => x +". "+ Y)".

var target = string.Join(".", source.Reverse().Select(c => c.ToString()).ToArray()); 
+0

Agradable, probablemente la mejor respuesta aquí, yo ... – Tracker1