Reputation: 345
I do have to iterate over a vector, which in turn has maps as its items. I need to compare which map comes next, and sometimes I need to look what was in the map we looked at before. So it is necessary to have some kind of look ahead/look behind functionality. My current approach works, but I guess it is ugly, unidiomatic Clojure and I assume that there must be a better (more canonical) way to achieve this.
(let [result (apply str (map (fn [{position-before :position compound-before :compund } ; previous term (unfortunately we need to compare all three)
{:keys [word position line tag stem compound grammarpos] :or {grammarpos "0" stem "" } } ; this maps to the current word
{position-ahead :position compound-ahead :compound line-ahead :line}] ; this is for lookahead
(do some stuff)) ;; now we have position, which is our current position, position-before and position-after to compare with each other
;; this is how we map:
(into '[{}] (conj grammar '[{}]))
(next (into '[{}] (conj grammar '[{}])))
(next (next (into '[{}] (conj grammar '[{}]))))))])
As for the request of the data-example, this is a part of the vector:
[{:tag "0", :position "0", :line "0", :stem "dev", :grammarpos "2625", :word "deva"} {:tag "0", :position "0", :line "0", :stem "deva", :grammarpos "4", :word "deva"}]
The job is to compare values for position, compound etc., sometimes look ahead, sometimes look behind.
Upvotes: 2
Views: 476
Reputation: 8593
If you want to do really complex things, perhaps zippers will be a better solution.
For example, lets say that you start with:
(def x
[{:tag "0" :dups 0}
{:tag "1" :dups 0}
{:tag "1" :dups 0}
{:tag "3" :dups 0}])
And your requirements are to increment the dups counter of all consecutive tags with the same name and add a "---" tag between them.
With zippers the solution will look like:
(require '[clojure.zip :as zip :refer [root node]])
(defn complex-thing [zip]
(if (zip/end? zip) ;; are we done?
(root zip) ;; return the "updated" vector
(let [current-node (node zip)
before-node (node (zip/prev zip))] ;; you can access any map in the vector, both before or after
(if (= (:tag current-node) (:tag before-node))
(recur (-> zip
zip/prev ;; move to the previous map
(zip/edit update :dups inc) ;; increment it
zip/next ;; move back to the current map
(zip/edit update :dups inc)
(zip/insert-left {:tag "----"}) ;; insert "---" before the current tag
zip/next)) ;; move to next map to process
(recur (zip/next zip))))))
(complex-thing (zip/next (zip/next (zip/vector-zip x)))) ;; start from the second element of the vector
[{:tag "0", :dups 0}
{:tag "1", :dups 1}
{:tag "----"}
{:tag "1", :dups 1}
{:tag "3", :dups 0}]
Upvotes: 1
Reputation: 17859
also if you need all the preceding and following items for every item, you can combine for
list comprehension, with destructuring.
for example:
user> (def items [:a :b :c :d :e :f :g])
#'user/items
user> (for [index (range (count items))
:let [[before [current & after]] (split-at index items)]]
{:before before :current current :after after})
({:before (), :current :a, :after (:b :c :d :e :f :g)}
{:before (:a), :current :b, :after (:c :d :e :f :g)}
{:before (:a :b), :current :c, :after (:d :e :f :g)}
{:before (:a :b :c), :current :d, :after (:e :f :g)}
{:before (:a :b :c :d), :current :e, :after (:f :g)}
{:before (:a :b :c :d :e), :current :f, :after (:g)}
{:before (:a :b :c :d :e :f), :current :g, :after nil})
you just split collection at every item's index one by one, and from the result take first item (before), first of second item (current), rest of second item (after)
also a bit less readable way (but probably more productive for big collection, since it doesn't do take
/drop
on every step, but adds/removes a single item to coll)
user> (take (count items)
(iterate
(fn [[before current after]]
[(conj before current) (first after) (rest after)])
[[] (first items) (rest items)]))
([[] :a (:b :c :d :e :f :g)]
[[:a] :b (:c :d :e :f :g)]
[[:a :b] :c (:d :e :f :g)]
[[:a :b :c] :d (:e :f :g)]
[[:a :b :c :d] :e (:f :g)]
[[:a :b :c :d :e] :f (:g)]
[[:a :b :c :d :e :f] :g ()])
Upvotes: 3
Reputation: 24771
You could iterate over a partition
of your vector, with a size of 3 and step of 1. Then for each element in the vector, you also get the before and after that you can study as you iterate with a for
or reduce
.
Some examples: https://clojuredocs.org/clojure.core/partition
Upvotes: 13