On Mon, Mar 7, 2011 at 1:14 PM, Tassilo Horn <tass...@member.fsf.org> wrote:
> Hello all,
>
> does clojure have sets that keep the insertion order, like Java's
> LinkedHashSet?
>
> Currently, I use lazy vectors in conjunction with `distinct', but that
> seems to perform not too well.
>
> Bye,
> Tassilo

Try something like:

(deftype OrderPreservingSet [v m s]
  clojure.lang.IObj
    (withMeta [this md] (OrderPreservingSet. v (with-meta m md) s))
    (meta [this] (meta m))
  clojure.lang.IPersistentSet
    (cons [this object]
      (if (contains? m object)
        this
        (OrderPreservingSet.
          (conj v object)
          (assoc m object (count v))
          s)))
    (count [this] (count m))
    (empty [this] (OrderPreservingSet. [] {} (Object.)))
    (equiv [this other] (= (seq this) other))
    (seq [this] (seq (remove #{s} v)))
    (get [this object]
      (v (m object)))
    (contains [this object]
      (contains? m object))
    (disjoin [this object]
      (if-let [idx (m object)]
        (OrderPreservingSet.
          (assoc v idx s)
          (dissoc m object)
          s)
       this))
  clojure.lang.IFn
    (invoke [this object]
      (get this object))
    (invoke [this object not-found]
      (get this object not-found)))

(defn order-preserving-set [& things]
  (reduce conj (OrderPreservingSet. [] {} (Object.)) things))

Cursory testing shows this working as expected (other than that
calling it as a function with the wrong arity will produce a different
exception from the usual IAE):

user=> (def foo (order-preserving-set 300 900 150 650 300))
#'user/foo
user=> foo
#{300 900 150 650}
user=> (def bar (Integer. 450))
#'user/bar
user=> (= bar (Integer. 450))
true
user=> (identical? bar (Integer. 450))
false
user=> (identical? ((conj foo bar) bar) bar)
true
user=> (disj foo 900)
#{300 150 650}
user=>

It should be substitutable for a normal set in Clojure code, but it
seqs and prints itself in insertion order. Performance caveat: if you
keep conjing and disjing items from an open-ended set the internal
vector may keep growing. The alternative isn't pretty though: actually
delete items from the internal vector, which would make disj O(n).

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to