Laden...

Im String sortieren mit Linq: Der richtige Platz für ein .Trim() ?

Erstellt von Hajoseb vor 8 Jahren Letzter Beitrag vor 8 Jahren 2.527 Views
Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 8 Jahren
Im String sortieren mit Linq: Der richtige Platz für ein .Trim() ?

Hallo an Alle.

Ich versuche gerade einige Sachen mit LINQ zu lösen (auch zur Übung).

Jetzt möchte ich, mit Komma getrennte Elemente, in einem String sortieren.
Ich finde aber nicht heraus, wo ich das .Trim() für die Elemente rein bekomme, oder sollte man vorher ein .Replace(" ", string.Empty) über den ganzen String machen?

.Trim() bei select ist ja zu spät, da ich die Elemente vor dem Sortieren trimmen will.

Wo, bzw. wie kann ich jedes Element vor dem Sortieren trimmen?


using System;
using System.Linq;

namespace SimpleTests
{
    class Program
    {
        static void Main(string[] args)
        {
            string originString = "en, de, fr,   xx , df";
            string resultString = string.Join(",", from languageShort in originString.Split(',')
                                                   orderby languageShort
                                                   select languageShort);

            Console.WriteLine("|" + resultString + "|");
            Console.ReadLine();
        }
    }
}

Als Ergebnis soll eigentlich |de,df,en,fr,xx| heraus kommen ...

So geht es zwar, aber eventuell kann man auf das temp verzichten ...


using System;
using System.Linq;

namespace SimpleTests
{
    class Program
    {
        static void Main(string[] args)
        {
            string originString = "en, de, fr,   xx , df";
            string resultString = string.Join(",", from languageShort in originString.Split(',')
                                                   let temp = languageShort.Trim()
                                                   orderby temp
                                                   select temp);

            Console.WriteLine("|" + resultString + "|");
            Console.ReadLine();
        }
    }
}

Das wäre mit .Replace ... aber ändert den original String... 😦


using System;
using System.Linq;

namespace SimpleTests
{
    class Program
    {
        static void Main(string[] args)
        {
            string originString = "en, de, fr,   xx , df";
            originString = originString.Replace(" ", string.Empty);
            string resultString = string.Join(",", from languageShort in originString.Split(',')
                                                   orderby languageShort
                                                   select languageShort);

            Console.WriteLine("|" + resultString + "|");
            Console.ReadLine();
        }
    }
}

oder so ...


using System;
using System.Linq;

namespace SimpleTests
{
    class Program
    {
        static void Main(string[] args)
        {
            string originString = "en, de, fr,   xx , df";
            string resultString = string.Join(",", from languageShort in originString.Replace(" ", string.Empty).Split(',')
                                                   orderby languageShort
                                                   select languageShort);

            Console.WriteLine("|" + resultString + "|");
            Console.ReadLine();
        }
    }
}

... gibt es ggf. bessere Wege für diese Aufgabe?

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

2.078 Beiträge seit 2012
vor 8 Jahren

Wenn Du die Query Syntax nutzen willst, gibt es keinen besseren Weg, als mit let ein Zwischenergebnis zu halten.

Wenn Du die Methoden nutzt, dann z.B. so:

string originString = "en, de, fr,   xx , df";

string resultParts = originString.Split(',')
                                 .Select(languageShort => languageShort.Trim())
                                 .OrderBy(languageShort => languageShort)

string resultString = string.Join(",", resultParts);
2.207 Beiträge seit 2011
vor 8 Jahren

Hallo Hajoseb,

der richtige Platz für ein Trim wäre nachdm du es in die Teile zerlegt hast, die man dann trimmen kann. Also nach einem Split.

var resultString = originString.Split(',').Select(x => x.Trim()).OrderBy(x => x).Aggregate((c, n) => c + ", " + n);

Fast Palladin007's Lösung 😃

Gruss

Coffeebean

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 8 Jahren
 string originString = "en, de, fr,   xx , df";  
            var resultString = originString.Split(',').Select(x => x.Trim()).OrderBy(x => x).Aggregate((c, n) => c + ", " + n);  
  
            Console.WriteLine("|" + resultString + "|");  
            Console.ReadLine();  

Ich steh eben auf (auch optisch) schönen Code 😉Hat denn das .Aggregate irgendeinen Vorteil gegenüber string.Join(...)

Danke für Eure Anregungen... 👍 Vermutlich gibt es keine "schöneren" Lösungen 😁

Versuch:

 
using System;
using System.Linq;

namespace SimpleTests
{
    class Program
    {
        static void Main(string[] args)
        { zur Optik
            string originString = "en, de, fr,   xx , df";
            var resultString = originString.Split(',')
                                           .Select(x => x.Trim())
                                           .OrderBy(x => x)
                                           .Aggregate((c, n) => c + "," + n);
            Console.WriteLine("|" + resultString + "|");
            Console.ReadLine();
        }
    }
}

Da musste noch das Leerzeichen nach dem "," weg ... 😉

Hinweis von Coffeebean vor 8 Jahren

Bitte beachte [Hinweis] Wie poste ich richtig? Punkt 2.3 keine Full Quotes

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

2.078 Beiträge seit 2012
vor 8 Jahren

Aggregate ist allgemeingültig für IEnumerable.

Ich denke, string.Join ist schneller als die Variante mit Aggregate.
Der Grund ist, dass intern der StringBuilder verwendet wird, der baut genau einen String.
Bei Aggregate würde mit jedem Durchlauf ein String erstellt und im RAM abgelegt.

Eine noch schnellere Variante wäre, vorher die Länge des neuen Strings zu berechnen und dem StringBuilder als Parameter zu übergeben.
Dafür gibt es allerdings keine Methode im .NET, zumindest keine die ich kenne.

Beides fällt aber nur auf, wenn es sich um größere Auflistungen handelt. Bei den 5 Elementen ist das ziemlich egal.

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 8 Jahren

Na, ja.

Hängt also von der Anzahl der nötigen Aufrufe ab.

thx

Hajoseb

P.S. Eh der ewige (innere) Konflikt, ob die Abfrage-Form oder die Lambda-Form ... :evil:

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

2.078 Beiträge seit 2012
vor 8 Jahren

Das meine ich nicht, der Nachteil fällt bei so wenig Elementen bloß nicht auf.

Wenn Du einen String erzeugst, was Du bei jedem Durchlauf in Aggregate machst, wird zuerst geschaut, ob es bereits einen vorhandenen String im RAM gibt und verwenden Diesen. Gibt es den nicht, wird er neu im RAM abgelegt.
Wenn Du das nun einige 1.000, 100.000 oder mehr hast, dann wird das auf einmal sehr interessant.
Bei 5 Elementen ist das vielleicht nicht einmal messbar.

String.Join macht das etwas anders.
Es hält im Innern einen StringBuilder. Jeder anzuhängende String wird im StringBuilder angehängt.
Der hält die Daten anders und kann am Ende einen einzelnen String erzeugen.

2.078 Beiträge seit 2012
vor 8 Jahren
var numbers = Enumerable.Range(0, 50000).Select(x => x.ToString());
var watch = new Stopwatch();

watch.Start();
var aggregateString = numbers.Aggregate((x, y) => x + "." + y);
watch.Stop();
Console.WriteLine(watch.ElapsedMilliseconds);

watch.Reset();

watch.Start();
var joinString = string.Join(".", numbers);
watch.Stop();
Console.WriteLine(watch.ElapsedMilliseconds);

Console.ReadKey();

Bei mir:

3901
9

Das ist ein stolzer Unterschied 😄

Wenn ich 5 Zahlen nehme, dann dauert die erste Variante eine Millisekunde, die Zweite unter 0.

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 8 Jahren

Hab noch eine Steigerung gefunden 😁


using System;
using System.Linq;

namespace SimpleTests
{
    class Program
    {
        static void Main(string[] args)
        {
            string originString = "en, de, fr,   xx , df";
            var resultString = originString.Split(',')
                                           .Select(x => x.Trim())
                                           .OrderBy(x => x)
                                           .Aggregate((c, n) => $"{c},{n}");
            Console.WriteLine($"|{resultString}|");
            Console.ReadLine();
        }
    }
}

C# 6 -> $"{...}"

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 8 Jahren

Das ist ein stolzer Unterschied 😁

Wenn ich 5 Zahlen nehme, dann dauert die erste Variante eine Millisekunde, die Zweite unter 0.

Wow !!! Du hast eine Zeitmaschine Programmiert 8o

unter 0, also kleine 0, also negative Zeit 8)

😁 Hajoseb

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 8 Jahren

Oha ....


            var numbers = Enumerable.Range(0, 50000).Select(x => x.ToString());
            var watch = new Stopwatch();

            watch.Start();
            var aggregateString = numbers.Aggregate((x, y) => x + "." + y);
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);

            watch.Reset();

            watch.Start();
            var joinString = string.Join(".", numbers);
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);

            watch.Reset();

            watch.Start();
            var newaggregateString = numbers.Aggregate((x, y) => $"{x}.{y}");
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);


4403
11
20059 !!!! X( 🤔

Ups ... das $"{....}" sollte man eventuell weniger Einsetzen ⚠

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

2.078 Beiträge seit 2012
vor 8 Jahren

Jaja, 0 Millisekunden bzw. nicht messbar 😄

Aus der String Interpolation wird ein String.Format gemacht. (So heißt das mit dem $"{....}")
Das ist dafür überhaupt nicht geeignet und auch nicht gedacht.
Außerdem umgeht es das Problem von Aggregate in diesem Anwendungsfall nicht.

Ich ziehe auch meine Theorie zum FastJoin zurück.
Sie ist zwar schnell, aber immer so rund 3 Millisekunden langsamer als string.Join
Zeigen darf ich den Code hier aber nicht, der gehört mir nicht 😄

Verwende einfach string.Join
Das ist genau dafür gemacht und auch im Detail optimiert.