2009-03-24 12 views
5

Oto prosty problem. Mam aplikację, która pobiera numer telefonu, taki jak "13335557777", i wymaga odwrócenia go i wstawienia kropki między każdą liczbą, na przykład:Problem z manipulowaniem ciągiem znaków C#

"7.7.7.7.5.5.5.3.3.3.1."

Wiem, że mogę to zrobić za pomocą StringBuilder i pętli for, aby odwrócić ciąg znaków i wstawić kropki, ale czy istnieje sprytny sposób na zrobienie tego w LINQ (lub w inny sposób)?

Uwaga: w tym przypadku nie zależy mi na wydajności, alokacji pamięci ani na czymkolwiek, tylko ciekawy, jak to zrobić w LINQ.

+0

Naprawdę chcesz kropkę po ostatnim 1? Jeśli tak, to nieznacznie zmienia problem. –

+0

Tak, to nie było jasne. To naprawdę nie ma znaczenia, wezmę rozwiązania, które robią to w obie strony. –

Odpowiedz

8

Try This

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

EDYCJA

To rozwiązanie jest zdecydowanie zmierzające do "sprytnego" końca. Prawdopodobnie o wiele bardziej wydajne będzie użycie StringBuilder do zbudowania łańcucha. To rozwiązanie tworzy wiele pośrednich ciągów.

EDIT2

Było trochę debata o względnej prędkości „sprytnego” rozwiązania vs. podejścia StringBuilder. Napisałem szybki test porównawczy, aby zmierzyć podejście. Zgodnie z oczekiwaniami StringBuilder jest szybszy.

  • normalny agregatów (100 elementy): 00: 00: 00,0418640
  • WithStringBuilder (100 elementów): 00: 00: 00,0040099
  • normalny kruszywa (1000 elementów): 00: 00: 00,3062040
  • WithStringBuilder elementy (1000): 00: 00: 00,0405955
  • kruszywa normalny (10000) elementy: 00: 00: 03,0270392
  • WithStringBuilder (10000 elementy) 00: 00: 00,4149977

Jednak to, czy różnica prędkości jest istotna, zależy w znacznym stopniu od miejsca, w którym jest faktycznie używana w aplikacji.

Kod testu porównawczego.

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

Dla informacji, podczas gdy "sprytny" i "FP", nie jestem pewien, czy to dobra rada ... liczba pośrednich ciągów oznacza, że ​​'StringBuilder' będzie bardziej pragmatyczny. –

+0

@Marc, zgodzili się, poprosili o sprytny, więc czułem się zobowiązany :) – JaredPar

+0

Tak, nie ma problemu, poprosiłem o sprytny kosztem czegokolwiek innego. –

0

(usunięty odpowiedź przy użyciu listy char)

Zgodnie z komentarzem na drugim poście, moim zdaniem jest to, że podczas gdy LINQ etc może być „mądry”, niekoniecznie jest to efektywne. Będzie to na przykład wiele pośrednich ciągów, które muszą zostać zebrane.

Będę trzymać się StringBuilder itp., Chyba że masz dobry powód, aby to zmienić.

+0

Dlaczego warto używać StringBuilder, gdy istnieje String.Join specjalnie do tego celu? –

+0

@Grauenwolf - ponieważ string.Join działa na ciąg [], a nie char [], więc musisz najpierw utworzyć ładunek łańcuchów. Dzięki StringBuilder nie musisz tego robić. –

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

"Wiem, że mogę to zrobić za pomocą StringBuilder i pętli for, aby odwrócić ciąg znaków i wstawić kropki, ale czy istnieje sprytny sposób na zrobienie tego w LINQ (lub w inny sposób)?" –

+0

Po prostu podałem mu kod, więc nie musiał tego pisać, palancie – Nick

+0

Jestem z tobą, Nick. +1 –

-1

Tak długo, jak już przeglądasz tablicę, łatwiej będzie użyć łańcucha.Zarejestrowany:

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

Pominąłeś pierwszy krok, rozbijając łańcuch na tablicę lub listę. –

+0

treść ciągu = "35557777"; to tablica :) –

+0

Powiedz mi, że nie głosowałeś na moją odpowiedź, a następnie przesyłasz własne, używając zasadniczo tej samej funkcji ... –

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

Czy to naprawdę problem LINQ? Po prostu pracuj wstecz w pętli na znak:

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

Jeśli jest to ciąg dłuższy niż 4k, użyj StringBuilder. Używanie LINQ dla "7.7.7.7.5.5.5.3.3.3.1." nie jest tym, za co LINQ jest, zaznacz mnie na -99, jeśli chcesz, i Marc też.

+0

Dzięki za odpowiedź. Wiem, że manipulowanie ciągami tak nie jest problemem LINQ, ale było to raczej pytanie typu "algorytm". Może byłoby lepiej, gdybym użył listy obiektów, a nie ciągu. –

2

Zaletą tego jest to, że String.Join będzie tańszy niż ".Aggregate ((x, y) => x +". "+ Y)".

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

Niezła, chyba najlepsza odpowiedź tutaj, imho ... – Tracker1

Powiązane problemy