[Snippet] Duplikate aus Sequenz entfernen

Hallo miteinander,


dann hauchen wir hier mal Leben ein. Das Snippet entfernt (wie der Titel schon sagt) Duplikate aus einer Sequenz und gibt nur die einzigartigen Werte zurück. Eingabe- und Ausgabewerte sind jeweils Sequenzen.

Ist nichts Weltbewegendes, aber ich benutzte es doch regelmässig.


Beispiel: myFunc([1,2,2,3,3,3,4,4,4,4] -> [1,2,3,4]

<#assign seq_out = [] />
<#list seq_in as valueIn>
<#if ! seq_out?seq_contains(valueIn)>
        <#assign seq_out = seq_out + [valueIn] />
    </#if>
</#list>
<#return seq_out>

Wenn das Im-/Export-Feature für die Snippets live geht, gibts das hier auch als Download.

Danke für den Input. Leider ist diese Art von der Sequence-Concatenation sehr langsam bei großen Sequences (siehe hier und hier).

Solltest du das hauptsächlich bei einer kleinen Anzahl Elementen wie in deinem Beispiel machen ist es ok, aber bei größeren Listen kann es sehr sehr langsam werden.


Wir haben das mal als Anregung mit aufgenommen, und schauen mal, dass wir da eine performante Funktion z.B. ${deduplicate(mylist)} liefern können.

Ich hatte vor der Variante mit Sequence-Concatenation eine mit Strings benutzt. Also statt direkt in eine Sequence die einzigartigen Werte zu schreiben, hängt der den Wert an einen String und schreibt irgendeine Trenn-String dahinter. Abschließend wird dann mit der split-Funktion die Sequence erstellt.

Ist das vielleicht schonender für die Synesty-Server?


Wenns das als Funktion "out-of-the-box" gibt, wäre natürlich toll.

Das ist jetzt schwer zu sagen - evtl. ist es schneller...

Lass es ruhig erstmal so. Wir haben die "out of the box" Funktion jetzt erstmal eingeplant für den nächsten Sprint. Wir haben da andere Möglichkeiten mit Java unter der Haube :) Dieser Fall hier ist leider eine sehr spezielle "Unschönheit" bzw. eine Designentscheidung von Freemarker, die negative Performanceauswirkungen hat.


Mit Java ist das einfacher zu lösen, das glaube ich gerne :D.


Ich hatte auch bereits ausprobiert, wie schnell die beiden Varianten so sind. Die Sequence-Variante braucht knapp über 6 Sekunden, während die String-Funktion in 15ms fertig ist.

Der Test ist zwar nicht super repräsentativ (1 Durchlauf bei 750 Werten und 250 einzigartigen Werten), aber die Größenordnung des Unterschieds spricht doch für sich.


Ich benutzt jetzt erstmal die String-Variante, bis die Funktion rauskommt (den Synesty-Servern zu liebe):

<#assign seperator = "--;--" />
<#assign string_out = "--;--" />
<#list seq_in as valueIn>
<#if ! string_out?contains("${seperator!}${valueIn!}${seperator!}")>
<#assign string_out = "${string_out}${valueIn}${seperator!}" />
    </#if>
</#list>
<#return string_out?keep_after(seperator!)?keep_before_last(seperator!)?split(seperator!)>



PS: Habe grade darüber nachgedacht, wie ich mein Snippet durch die Funktion ersetzen würde, wenn sie denn live geht.

Dafür muss ich ja jede Stelle finden, wo ich mein Snippet aufrufe. Habt ihr da bereits sowas ähnliches wie "Referenzierende Flows anzeigen" beim DataStore geplant? Das man auf einer Seite alle Aufrufe von einem Snippet sehen kann?

Wir haben jetzt eine neue Template-Funktion deduplicate eingefügt. Diese sollte wesentlich schneller sein, als die reine Freemarker-Lösung, weil wir "unter der Haube" andere Möglichkeiten haben.


Beispiel:

${deduplicate([2,3,2,1,1,4,5])!}


Ergebnis: [2,3,1,4,5]


${deduplicate(["a","b","a","c"])!}


[a,b,c]


${deduplicate("a,b,a,c", ",")!}

[a,b,c]



Hallo Zusammen,


Ich fand das Thema sehr interessant und habe mich dazu kurz eingelesen.

Wenn Hashes in Freemarker miteinander konkateniert werden (<#assign seq_out = seq_out + [valueIn] />), dann entsteht daraus kein Hash, welches nur die rohen Werte enthält. Statt dessen wird ein Hash erstellt, dass Referenzen zu den beiden input Hashes beinhaltet.

Hat man zum Beispiel <#assign a = [2, 4]> und <#assign b = [6, 8]> und macht dann daraus <#assign c = a + b> dann ist der Inhalt von Hash c = a,b und nicht c = 2, 4, 6, 8.

Wenn man jetzt also per seq_contains nach dem Wert "6" sucht, dann muss Freemarker jeden Hash innerhalb von c aufrufen und überprüfen, ob sich darin der gewünscht Wert befindet.

Das kann dann beliebig oft verschachtelt werden.

Man könnte jetzt denken, dass hier aber nur das bestehende Hash immer bearbeitet wird, weil ja <#assign value = value + x> gemacht wird. Das hat aber bei Freemarker den selben Effekt wie <#assign c = a + b>, da Hashes in sich nicht veränderbar sind. Das heißt es muss immer ein neues Hash generiert werden wenn ein Hash bearbeitet werden soll.


Die Methode mit der String Konkatenation funktioniert aber wohl recht schnell, weil hier beim Splitten nicht Hashes mit Referenzen, sondern Hashes die nur rohe Werte beinhalten generiert werden.

Diese lassen sich weit aus schneller durchsuchen, als Hashes, die Referenzen zu anderen Hashes beinhalten.


Viele Grüße

Stefan