sclass StringClustersWithIDs { new L clusters; !include #1025301 // change()/onChange() sclass Cluster { GlobalID globalID = aGlobalIDObject(); Set synonyms = ciSet(); *() {} *(S s) { synonyms.add(s); } toString { ret "Cluster " + globalID + ": " + synonyms; } bool add(S s) { if (!synonyms.add(s)) false; ret true with change(); } } L searchForCluster(S s) { ret filter(clusters, c -> contains(c.synonyms, s)); } Cluster clusterWith(S s) { ret first(searchForCluster(s)); } void add(Cluster cluster) { clusters.add(cluster); change(); } void remove(Cluster cluster) { clusters.remove(cluster); change(); } // also adds to list Cluster newCluster(S s) { Cluster c = new(s); add(c); ret c; } // also adds to list Cluster newCluster(Cl synonyms) { new Cluster c; addAll(c.synonyms, synonyms); add(c); ret c; } // also merges clusters void addPair(S a, S b) { Cluster c1 = clusterWith(a), c2 = clusterWith(b); if (c1 != null) if (c2 != null) mergeClusters(c1, c2); else c1.add(b); else if (c2 != null) c2.add(b); else newCluster(ll(a, b)); } void mergeClusters(Cluster a, Cluster b) { a.synonyms.addAll(b.synonyms); remove(b); } }