user3602477
user3602477

Reputation: 99

Clojure Atoms, I want to return the final value

Ok so put into my function an array an I keep so do some stuff to return a hashmap with a string as key and a list as value. I want to get only the final value and pass it as a parameter to another function.

Below is a java code which i am trying to translate into clojure

contents = documentContent.split("[ ,\\.]+");
        for(int i = 0; i < contents.length; i++) {
            String word = contents[i];
            if(index.containsKey(word)) {
                index.get(word).add(i);
            } else {
                List<Integer> list = new ArrayList<>();
                list.add(i);
                index.put(word, list);
            }
        }

Clojure code so far it somewhat works but i cant return the final value and pass it to another function

(defn indexFinder [contents] 
  (let [mem (def xs (atom {}))]
    (map-indexed
      (fn [index element]
        (if (contains? @xs element)
          (swap! xs assoc element (conj (@xs element) index))
          (swap! xs assoc element [index])))
      contents)))

Upvotes: 0

Views: 210

Answers (3)

nha
nha

Reputation: 18005

Here is how I would write it:

(defn index-finder [s]
  (->> (clojure.string/split s #"[ ,\\.]+")
       (map-indexed list)
       (reduce (fn [acc [i w]]
                 (update acc w conj i)) {})))

Example use:

(index-finder "aaa bbb ccc ddd aaa bbb") ;; {"aaa" (4 0), "bbb" (5 1), "ccc" (2), "ddd" (3)}

Note: when starting learning Clojure, one tends to think that he needs atoms way to often.

Upvotes: 2

rmcv
rmcv

Reputation: 1976

The original indexFinder is missing a doall to force its side effects (to update the atom). And also declaration of mem is not required. With that, a working version may look like this:

(defn indexFinder [contents] 
  (let [xs (atom {})]
    (doall (map-indexed
            (fn [index element]
              (if (contains? @xs element)
                (swap! xs assoc element (conj (@xs element) index))
                (swap! xs assoc element [index])))
            contents))
    @xs))

You can also simplify the map-indexed part:

(defn indexFinder [contents] 
  (let [xs (atom {})]
    (doall (map-indexed
            (fn [i e] (swap! xs update e conj i))
            contents))
    @xs))

Or if you don't want to rely on side effects, you can use this instead (similar to Brandon's answer but use group-by):

(defn index-finder [contents]
  (->> (zipmap (range) contents)
       (group-by last)
       (fmap (partial map first))))

where fmap is used to map a function over values (see algo.generic):

(defn fmap [f m]
  (into (empty m) (for [[k v] m] [k (f v)])))

Upvotes: 1

Brandon Henry
Brandon Henry

Reputation: 3720

I'd recommend against the atom in the first place.

(->> (zipmap (range) contents) ;contents is a list of words
     (reduce (fn [index [location word]]
               (merge-with concat index {word (list location)})) {}))

example:

user=> (def contents (clojure.string/split "hello there these are some words and some repeated words" #" "))
#'user/contents
user=> contents
["hello" "there" "these" "are" "some" "words" "and" "some" "repeated" "words"]

user=>     (->> (zipmap (range) contents)
  #_=>          (reduce (fn [index [location word]]
  #_=>                    (merge-with concat index {word (list location)})) {}))
{"hello" (0), "some" (7 4), "there" (1), "and" (6), "are" (3), "these" (2), "words" (9 5), "repeated" (8)}

Upvotes: 1

Related Questions