1

I am new to clojure programming and would like some help with some code. I have this vector of vectors like below,

(def start-pos [[[:fox :goose :corn :you] [:boat] []]])

I would like to loop through the vector and remove an element from one of the internal vectors, e.g. remove ':goose' from start-pos.

I tried the code below but for some reason it doesnt work as intended,

(map #(disj (set %) :goose) start-pos)

Instead the result is,

(#{[:boat] [] [:fox :goose :corn :you]})

As you can see from the result, the internal vectors are now a set and yes, the original order is distorted, is there a way of removing the element and not disarrange the original order of the vectors, maybe without converting it to a set first? I choose this conversion to a set first because according to the docs disj only works for sets.

Add: This post is not similar to this suggested post as my vector is nested three vectors deep.

1

3 Answers 3

2

the internal vectors are now a set

That's because the result of #(disj (set %) :goose) returns a set.

original order is distorted

Sets don't preserve insertion order by default, similar to maps with over 8 keys.

I would like to loop through the vector and remove an element from one of the internal vectors, e.g. remove ':goose' from start-pos.

The function you need for removing an element from a collection by predicate is called remove, but...

The value you want to remove is actually nested three vectors deep in start-pos, so you'd need an additional iteration for each inner vector, and so on if you wanted to remove the keyword :goose from every vector recursively. That's an excuse to use clojure.walk:

(clojure.walk/postwalk
  (fn [v]
    (if (coll? v)
      (into (empty v) (remove #{:goose}) v)
      v))
  start-pos)
=> [[[:fox :corn :you] [:boat] []]]

This walks every value in start-pos, removing :goose from any collections it finds.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for this reply, works. I also realised that nesting the nesting of the vector three times was a mistake and instead what i intended was (def start-pos [[:fox :goose :corn :you] [:boat] []]) . After that my code worked. Thank you for this, will definitely need it in the future
1

Here is a less flexible approach, that I made more so for my own benefit (learning Clojure)

(update-in
  start-pos
  [0 0]
  #(vec (concat
          (subvec % 0 1)
          (subvec % (inc 1)))))

It manually navigates in and reconstructs the :goose level of keywords to not have :goose inside

I think some alternative approaches to this problem include Specter and Zippers

1 Comment

definitely look into Spectre which makes transformations like this very easy, barely an inconvenience
1

you could also employ clojure zipper for that:

user> (require '[clojure.zip :as z])

user> (loop [curr (z/vector-zip start-pos)]
        (cond (z/end? curr) (z/root curr)
              (= :goose (z/node curr)) (recur (z/remove curr))
              :else (recur (z/next curr))))
;; => [[[:fox :corn :you] [:boat] []]]

also, that is quite easy to do with clojure's core functions only:

user> (defn remv [pred data]
        (if (vector? data)
          (mapv (partial remv pred) (remove pred data))
          data))
#'user/remv

user> (remv #{:goose} start-pos)
;; => [[[:fox :corn :you] [:boat] []]]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.