2
votes

I am new to clojure programming and need a little help. Say I have a vector of maps as shown.

(def testVMap 
  [ {:name "AAA" :rate 100 } 
    {:name "GEICO" :rate 120 }
    {:name "PROGRESSIVE" :rate 118} ] )

How do I do some operations like finding the rate of AAA or to see if I have the rates for, say ALLSTAR, if rates are not present I would want to able to add it, I know I can add that using into or conj. But my main concern is finding my map has a particluar name and if name is present I need to update the rate to a new value.

3

3 Answers

2
votes

Here's one method:

(some #(if (= "GEICO" (:name %)) %) testVMap)

That says: Return the first thing in testVMap whose :name is equal to "GEICO". #(... % ...) defines a function, with % as its function parameter. This part (:name %) returns the value of the :name value for each map.

If there might be multiple "GEICO" maps, and you want them all, use filter instead of some.

Here's another version:

 (some #(and (= "GEICO" (:name %)) %) testVMap)

You may want to use update as well.

EDIT: e.g.:

(map #(if (= "GEICO" (:name %))
          (update % :rate inc)
          %)
testVMap)

Here inc is the function that adds 1 to a number. You should replace that with a function that performs the transformation that you want, such as #(+ 10 %), or (partial + 10), which is the same thing.

The function defined by #(...) checks whether the value of :name is "GEICO", and then if so, updates the rate with the function that you provide (inc in this case); otherwise, #(...) returns the map unchanged.

I'll leave it to you to look up the documentation of update, if it's not self-explanatory from the context.

You could also use mapv instead of map.

You'll no doubt want to build all of this into one or more functions. Remember that functions are things in Clojure--they're "data", first class objects, etc. So where I used inc, you can have a function parameter, and the function you define can accept another function as its argument, which the first function will then use in the update part of the code (instead of inc).

About adding a map if the one you want doesn't exist: If you're not dealing with a long sequence of maps, I'd consider doing this in a different step. See Andre's answer for names of functions that are useful for testing whether a collection contains an element.

However, note that testing whether a vector contains something that's not there requires Clojure to look through the entire vector. It might be better to use a Clojure set rather than a vector, or consider using an outer map with (for example) :aaa, :geico, etc. as keys. In that case, take a look at the function update-in.

1
votes

Instead of telling you straight away how to do this, let me show you how you'd find this kind of function.

These two website structure the functions pretty well:

Now what you're dealing with is in general a collection and if that doesn't do what you want you can look for specific vector operations. But the first should be collection. You'll then see Content Tests section in both sites.

Now you have the following candidates:

distinct? empty? every? not-every? some not-any?

The right candidate here is some:

(some (comp (partial = "AAA") :name) testVMap)
;; => true
1
votes

Seeing if an item exists

(first
  (filter
    (comp #{"AAA"} :name)
    [{:name "AAA" :rate 100}
     {:name "GEICO" :rate 120}
     {:name "PROGRESSIVE" :rate 118}]))

Updating your structure

If order isn't important, you can reorganize the structure into a map of name->rate. For example:

{"AAA" 100 "GEICO" 120}

Then you can use update-in or assoc to update the rate.

If order is important, you can do something like this:

(mapv
  (fn [{:keys [name] :as v}]
    (if (= name "AAA")
      (assoc v :rate 500)
      v))
  [{:name "AAA" :rate 100}
   {:name "GEICO" :rate 120}
   {:name "PROGRESSIVE" :rate 118}])

Adding to your structure

You can add to it with a simple conj. So you do something like

(if (has-item? struct) ;; has-item would be defined using the first section of this answer
  (update-item struct ...) ;; update-item using the second section of this answer
  (conj struct {:name "AAA" :rate 100}))

There are other options of course, but these would be the first that I would go to.