0

split or sort-function bug?

Hallo zusammen,

der Code
 

split(concat(for l in thisSchKJ do
            let diff := number(l.Wochentag) - 1 - weekday(l.Kursjahrgang.'Beginn Vorkurs');
            concat(for m in range(0, AnzWeek + 1) do
                    if l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m >= Kursjahrgang.'Beginn Vorkurs' and
                        l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m <= l.Kursjahrgang.'Ende Vorkurs' then
                        l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m
                    end
                end)
        end), ",")

gibt in einem f-Feld die 6 korrekten Datumsangaben als Text-Array aus.

Schalte ich ein sort() um den ganzen Code erscheint die Sortierung nicht korrekt (siehe rechts). Es sieht so aus als ob der 30.1. und 16.1. als ein Arrayeintrag aufgefasst werden. Auffällig auch das Leerzeichen vor dem 11.1.

Kann das jemand nachvollziehen? Mache ich einen Denkfehler? Korrekter Code? Eine Idee für einen Workaround?

Grüße Maurice

9 Antworten

null
    • UweG
    • vor 1 Monat
    • Gemeldet - anzeigen

    Mir fehlen da noch mehr Informationen.
    Aber du nutzt 2 Schleifen und die produzieren immer ein Array.
    Wie der Inhalt der Arrays aussieht wäre sehr hilfreich.
    Hast du dir mal mit debugValueInfo() ausgeben lassen, wie das Ergebnis der ersten und auch der zweiten Schleife aussehen?
    Man sollte sich immer ansehen können, was Teile eines Scripts an Informationen produzieren, damit man Rückschlüsse auf Fehler ziehen kann.
    Gutes Debugging ist der Schlüssel für gutes Programmieren.

      • Maurice
      • vor 1 Monat
      • Gemeldet - anzeigen

       Danke für die Rückmeldung. In der Tat mit debugValueInfo habe ich bisher nicht gearbeitet, meist mit typeof(). Fangen wir mit der inneren Schleife an, die ein merkwürdiges Ergebnis bringt in der Struktur. Code ist jetzt:

      sort(split(concat(for l in thisSchKJ do
                  let diff := number(l.Wochentag) - 1 - weekday(l.Kursjahrgang.'Beginn Vorkurs');
                  concat(debugValueInfo(for m in range(0, AnzWeek + 1) do
                          if l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m >= Kursjahrgang.'Beginn Vorkurs' and
                              l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m <= l.Kursjahrgang.'Ende Vorkurs' then
                              l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m
                          end
                      end))
              end), ","))
      

      Und das Ergebnis (Ausschnitt):

      date([1704931200000,1705536000000,1705968000000,1706140800000]),1706572800000]),date([1705363200000
      

      Wie kommt es, dass das zweite Element hinten "umgebrochen" ist?

      • UweG
      • vor 1 Monat
      • Gemeldet - anzeigen

       
      sort(split(replace(concat(for l in thisSchKJ do
                  let diff := number(l.Wochentag) - 1 - weekday(l.Kursjahrgang.'Beginn Vorkurs');
                  for m in range(0, AnzWeek + 1) do
                          if l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m >= Kursjahrgang.'Beginn Vorkurs' and
                              l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m <= l.Kursjahrgang.'Ende Vorkurs' then
                              l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m
                          end
                      end
              end), "  ","") , ","))
       

      • Maurice
      • vor 1 Monat
      • Gemeldet - anzeigen

       Dann erhalte ich eine leer Anzeige. Setze ich um deinen Code ein typeof() erhalte ich als Ausgabe [string]. Wenn ich dann ein concat() statt typeof setze, müsste ja ein string, der zu sehen ist, herauskommen, aber dann ist immer noch nichts zu sehen. Erst mit einem concat in der inneren Schleife sind überhaupt Termine sichtbar.

      • UweG
      • vor 1 Monat
      • Gemeldet - anzeigen

       
      Das Problem liegt in dem Datentyp.
      Du wandelst ein Datum mit concat in einen String um.
      Das wandelst du dann mit Split wieder in ein Array um, welches jetzt aber ein Textarray ist und kein Datumsarray mehr.
      Wenn du dann sort() anwendest, wird nach dem Typ Text und nicht nach dem Typ Datum sortiert und man bekommt ein falsches Ergebnis.
      Die andere Schwierigkeit ist das verschachtelte Array als Ergebnis der ersten Schleife.

      Deshalb ist die Funktion debugValueInfo() mit eine der wichtigsten Funktionen für das Debugging von Scripten.
      Ein alter Grundsatz beim programmieren lautet:
      Niemals Äpfel mit Birnen vergleichen. Sortenreines arbeiten ist oberstes Gebot.
       

      let ArrDat := split(string(for l in thisSchKJ do
                  let diff := number(l.Wochentag) - 1 - weekday(l.Kursjahrgang.'Beginn Vorkurs');
                  for m in range(0, AnzWeek + 1) do
                          if l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m >= Kursjahrgang.'Beginn Vorkurs' and
                              l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m <= l.Kursjahrgang.'Ende Vorkurs' then
                              l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m
                          end
                      end
              end), ",");
      concat(sort(for n in ArrDat do
      date(number(n))
      end
      • Maurice
      • vor 1 Monat
      • Gemeldet - anzeigen

       Hallo Uwe, herzlichen Dank. Wieder etwas gelernt. Sortenreines Programmieren. Habe jetzt deinen Code nachvollziehen können. Es funktioniert.

      Was ich nicht verstehe: Warum muss zu Beginn oben ein split(string...)) gesetzt werden, also ein Textarray erzeugt werden, wenn ich unten mit date(number(n)), wieder ein Datum daraus mache, das ich sortiere. Ohne split(string)...) funktioniert der Code nicht, warum?

      Auch wenn jeder, der solchen Code verwendet, das wahrscheinlich leicht sehen wird, trotzdem der Vollständigkeit halber für die Nachwelt: Nach dem letzten "end" in Uwes Code müssen noch zwei geschlossene Klammern gesetzt  werden.

      • UweG
      • vor 1 Monat
      • Gemeldet - anzeigen

       

      debugValueInfo(for l in thisSchKJ do
                  let diff := number(l.Wochentag) - 1 - weekday(l.Kursjahrgang.'Beginn Vorkurs');
                  concat(for m in range(0, AnzWeek + 1) do
                          if l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m >= Kursjahrgang.'Beginn Vorkurs' and
                              l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m <= l.Kursjahrgang.'Ende Vorkurs' then
                              l.Kursjahrgang.'Beginn Vorkurs' + diff + 7 * m
                          end
                      end)

      Mit dem Script erhält du ein verschachteltest Array. [[], []]
      Darauf kann man kein sort() anwenden, da ea nicht alle Werte des Arrays sortiert, sondern jedes Array für sich.
      Deshalb mache ich aus den vielen Array's erst einen kommaseparierten String, um daraus wieder nur ein Array statt vieler Arrays zu erstellen.
      Dabei bleibt jedoch der ursprüngliche Datentyp auf der Strecke und eine Sortierung würde in diesem Zustand fehlerhaft sein. (Textsortierung - 11.09.2024 ist vor dem 12.06.2023)
      Mit ein Hauptgrund, weshalb Anfragen kommen, warum meine mit format() definierten Werte falsch sortiert werden. (Auf den Datentyp achten!!! - format() ergibt immer Text)
      Da jetzt ein Text-Array vorliegt, wird in einer Schleife der Text in eine Zahl umgewandelt und diese mit date() wieder in ein Datum.
      Wenn ich jetzt sort() benutze, erhalte ich die korrekte aufsteigende Sortierung. (12.06.2023 liegt vor 11.09.2024)

      Und nochmals der Hinweis wie wertvoll die Funktion debugValueInfo() bei der Entwicklung sein kann.
      Auch eine eigen DebugPage ist bei Skripten die serverseitig ausgeführt werden sehr nützlich.
      Dort helfen alert() und dialog() nicht, weil diese nicht angezeigt werden bei Ausführung des Script.
      Hingegen hilft record(DebugPage,1).mehrzeiligesTextfeld1 := debugValueInfo(Variable, select, Schleife, etc)
      Da kann man nachsehen, was im serverseitigen Script produziert wird.
      So eine DebugPage habe ich in allen meinen Entwicklungsdatenbanken und bei Datenbanken, die ich untersuche. Oder ich nutze debug() und den Entwicklermodus im Browser.
       

      • Maurice
      • vor 1 Monat
      • Gemeldet - anzeigen

       Danke für deine Geduld und die ausführliche Antwort. Die erklärt jetzt auch, warum an anderer Stelle bei mir das Script

      join(rsort(for k in ZeitschienenAusschnitt do
              let thisSchKJ := first((select Zeitschienen)[ID = k]);
              let AnzWeek := ceil(days('Beginn Kontinuum', 'Ende Kontinuum') / 7);
              let diff := number(thisSchKJ.Wochentag) - 1 - weekday('Beginn Kontinuum');
              (concat(sort(for m in range(0, AnzWeek + 1) do
                          if 'Beginn Kontinuum' + diff + 7 * m >= 'Beginn Kontinuum' and
                                          'Beginn Kontinuum' + diff + 7 * m <= 'Ende Kontinuum' and
                                      not contains(thisArray, 'Beginn Kontinuum' + diff + 7 * m) and
                                  not ('Beginn Kontinuum' + diff + 7 * m >= 'Exkursion Beginn' and
                                          'Beginn Kontinuum' + diff + 7 * m <= 'Exkursion Ende') and
                              'Beginn Kontinuum' + diff + 7 * m != LPT then
                              'Beginn Kontinuum' + diff + 7 * m
                          end
                      end)) +
              "
      ")
          end), "")

      funktioniert hat. Da habe ich nicht die Doppel-Array-Struktur. Und meine Beschränkung auf die Analyse mit typeof() statt DebugValueInfo() brachte in beiden Fällen nur ein [string] hervor. Einmal ist es aber ein Doppel-Array und einmal ein einfaches Array. Das funktioniert hier, weil das innere sort() die Datumseinträge sortiert, das concat einen Text daraus macht und das äußere rsort() über k sich auf Textinhalte bezieht und nicht auf Datumsfelder. Sonst würde das auch ins "Leere" laufen bzw. eine textbasierte und nicht datumsbasierte Sortierung erzeugen.

      Die Idee/Maßnahme mit der DebugPage ist klasse. Ich habe meist mit lokalen Funktionsfeldern an entsprechender Stelle gearbeitet, aber eine DebugPage macht das ganze strukturell besser und professioneller.

      Dass format() immer Text ergibt, war mir auch nicht klar. Ist natürlich dann wichtig, wenn man auf mit format gesetzte Funktionsfelder zugreift und vermeintlich meint, das wären Datumtypen, die man dort abgreift. Deshalb wohl deine Einlassung hier. Genau das wird jetzt wohl auch auf mich zukommen. Ich sitze an einer recht komplexen Raumbuchung und greife auf versch. Felder mit Datum- und Zeitausgaben zu. Das werde ich mir genau anschauen müssen. Und während ich das schreibe, ist mir dann auch der Unterschied zwischen

      date(number(text(date(2023, 12, 6))))

      und

      date(number(string(date(2023, 12, 6))))

      klar geworden (kann jeder ausprobieren). Im ersten Fall erhält man den 01.01.1970 zurück, weil number(text(date())) eine 0 erzeugt, im zweiten Fall erhält man den 06.12.2023 zurück, weil string() den Hexedezimalwert des Datums erzeugt.

      Das war für mich, der bisher relativ wenig mit Datumsfeldern zu tun hatte, (meist nur Eingabe, Ausgabe oder mal Dauer etc. berechnen) ein sehr lehrreicher Thread. Hoffe, der "Nachwelt" bringt dieser Thread auch etwas. Leider passt der Titel des Threads nicht mehr ganz zu dem er sich entwickelt hat. Es geht ja auch um den grundlegenden Umgang mit Datumsfeldern und Vorgehensweisen bei der grundsätzlichen Analyse.

      Nochmals herzlichen Dank.

      • UweG
      • vor 1 Monat
      • Gemeldet - anzeigen

       Danke für die ausführliche Antwort.

      Du schreibst:
      Ist natürlich dann wichtig, wenn man auf mit format gesetzte Funktionsfelder zugreift und vermeintlich meint, das wären Datumtypen, die man dort abgreift.'

      Genau DAS solltest du vermeiden.
      Das Zugreifen auf Formelfelder zu weiteren Auswertungen ist ein NoGo.
      FormelFelder sollten NIEMALS als  Datenfelder benutzt werden.
      Zum Anzeigen von Informationen bei einem Dashboard ist es OK aber nicht um sie bei Auswertungen weiter zu benutzen.
      Eine simple Berechnung in einem Formelfeld sum(select Tabelle.Ausgabe) mag in der Formularansicht keine große Performanceauswirkung haben. Wenn du aber eine Tabellenauswertung fährst, wo du Bezug auf das Formelfeld nimmst, wird bei jedem einzelnen Record der Tabelle die Formel neu berechnet. Wenn dann noch Schleifen dazu kommen, dann potenzieren sich die Berechnungen dermaßen, dass du möglicherweise Minutenlang den 'Kreisel des Todes' zu sehen bekommst.

      Wenn du damit weiter arbeiten musst, dann nutze statt dem Formelfeld ein entsprechendes Datenfeld.
      Das kann man auch über ÄnderungsTrigger der entsprechenden Bezugsfelder aktuell halten.
      Ist zum Anfang etwas mehr Aufwand und manchmal etwas Tricky, zahlt sich aber bei der Performance erheblich aus.
      Mit Anwachsen der Datenmenge kannst du dir durch Formelfelder, auf die Bezug genommen werden muss bei nachfolgenden Auswertungen, deine gesamte Performance versauen.
      Da hilft dann auch kein Beschweren beim Support, dass die DB so elendig langsam ist.
      Das hat in 95% der Anfragen einzig mit einem nicht gut durchdachten Datenbankdesign und fehlerhaften Scripting zu tun.