0
votes

I have the following data structure (two Vectors of Maps I believe)

{ :put [{:listId "myList", :entryId "1", :dateTime 1434637074},
        {:listId "myList", :entryId "2", :dateTime 1434637075}],
  :delete [{:listId "myList", :entryId "1"}]}

and would like to remove any :put entries for which the keys listId and entryId already exist inside :delete. The result should therefore be:

{ :put [{:listId "myList", :entryId "2", :dateTime 1434637075}],
  :delete [{:listId "myList", :entryId "1"}]}

I have tried the following but 1) it's not idiomatic Clojure and 2) it doesn't delete the duplicate entry.

(defn remove-add-if-duplicate [requests]
  (doseq [delete (seq (:delete requests))]
    (doseq [put (seq (:put requests)) 
            :when (and (= (:listId delete) (:listId put))
                       (= (:entryId delete) (:entryId put))]
      (disj (set (:put requests)) put)))
  requests))

(remove-add-if-duplicate 
  { :put [{:listId "myList", :entryId "1", :dateTime 1434637074},
          {:listId "myList", :entryId "2", :dateTime 1434637075}],
    :delete [{:listId "myList", :entryId "1"}]})

Can you help me fix my code or show me a nice way of doing this?

2

2 Answers

2
votes
(defn strip [data]
  (let [ks (-> data :delete set)]
    (update-in data [:put] #(remove (fn [m] (ks (select-keys m [:listId :entryId]))) %))))

For example, ...

(def my-stuff { :put [{:listId "myList", :entryId "1", :dateTime 1434637074},
        {:listId "myList", :entryId "2", :dateTime 1434637075}],
  :delete [{:listId "myList", :entryId "1"}]})

(def processed { :put [{:listId "myList", :entryId "2", :dateTime 1434637075}],
  :delete [{:listId "myList", :entryId "1"}]})

(= (strip my-stuff) processed);
;true

This is essentially the same as McSokoli's answer, but

  • presented as a function,
  • using a set rather than some to test for presence,
  • using update-in to apply to the whole data structure.

You have misunderstood the way that Clojure's data structures work. The functions that handle them don't change them; instead, they return a modified version.

Your use of doseq is a giveaway. This returns nil, so only has side effects, of which there are none in this case; nor ought there to be.

You could use for with :where to get the effect of remove, but there's no point.

1
votes

This should do the job. We use select-keys to keep only the keys (and their values) :listId and :entryId from entries in :put from the input, and remove such entries that are found in (:delete input).

(def input {:put [{:listId "myList", :entryId "1", :dateTime 1434637074},
                  {:listId "myList", :entryId "2", :dateTime 1434637075}],
            :delete [{:listId "myList", :entryId "1"}]}


(defn filtered [input]
  {:delete (:delete input)
   :put (remove #(some #{(select-keys % [:listId :entryId])} 
               (:delete input))    
        (:put input))})

Testing it we get this:

user> (filtered input)
{:delete [{:listId "myList", :entryId "1"}], 
 :put ({:listId "myList", :entryId "2", :dateTime 1434637075})}

or with more data

user> (filtered {:put [{:listId "myList", :entryId "1", :dateTime 1434637074},
                       {:listId "myList", :entryId "2", :dateTime 1434637075}
                       {:listId "other", :entryId "3", :dateTime 1434637076}
                       {:listId "bestList", :entryId "4", :dateTime 1434637076}],
                :delete [{:listId "myList", :entryId "1"}
                         {:listId "bestList" :entryId "4"}]})
{:delete [{:listId "myList", :entryId "1"} {:listId "bestList", :entryId "4"}],
 :put ({:listId "myList", :entryId "2", :dateTime 1434637075}
       {:listId "other", :entryId "3", :dateTime 1434637076})}