3
votes

I have a vector of vectors that contains some strings and ints:

(def data [
["a" "title" "b" 1]
["c" "title" "d" 1]
["e" "title" "f" 2]
["g" "title" "h" 1]
])

I'm trying to iterate through the vector and return(?) any rows that contain a certain string e.g. "a". I tried implementing things like this:

(defn get-row [data]
  (for [d [data]
        :when (= (get-in d[0]) "a")] d
  ))

I'm quite new to Clojure, but I believe this is saying: For every element (vector) in 'data', if that vector contains "a", return it?

I know get-in needs 2 parameters, that part is where I'm unsure of what to do.

I have looked at answers like this and this but I don't really understand how they work. From what I can gather they're converting the vector to a map and doing the operations on that instead?

4

4 Answers

4
votes
(filter #(some #{"a"} %) data)

It's a bit strange seeing the set #{"a"} but it works as a predicate function for some. Adding more entries to the set would be like a logical OR for it, i.e.

(filter #(some #{"a" "c"} %) data)
=> (["a" "title" "b" 1] ["c" "title" "d" 1])
1
votes

ok you have error in your code

(defn get-row [data]
  (for [d [data]
        :when (= (get-in d[0]) "a")] d
  ))

the error is here: (for [d [data] ... to traverse all the elements you shouldn't enclose data in brackets, because this syntax is for creating vectors. Here you are trying to traverse a vector of one element. that is how it look like for clojure:

    (for [d [[["a" "title" "b" 1]
              ["c" "title" "d" 1]
              ["e" "title" "f" 2]
              ["g" "title" "h" 1]]] ...

so, correct variant is:

(defn get-row [data]
  (for [d data
        :when (= "a" (get-in d [0]))]
    d))

then, you could use clojure' destructuring for that:

(defn get-row [data]
  (for [[f & _ :as d] data
        :when (= f "a")]
    d))

but more clojuric way is to use higher order functions:

(defn get-row [data]
  (filter #(= (first %) "a") data))

that is about your code. But corretc variant is in other guys' answers, because here you are checking just first item.

0
votes
(defn get-row [data]
  (for [d data ; <-- fix: [data] would result 
               ; in one iteration with d bound to data
        :when (= (get-in d[0]) "a")]
    d))

Observe that your algorithm returns rows where the first column is "a". This can e. g. be solved using some with a set as predicate function to scan the entire row.

(defn get-row [data]
  (for [row data
        :when (some #{"a"} row)]
    row))
0
votes

Even better than the currently selected answer, this would work:

(filter #(= "a" (% 0)) data)

The reason for this is because for the top answer you are searching all the indexes of the sub-vectors for your query, whereas you might only wantto look in the first index of each sub-vector (in this case, search through each position 0 for "a", before returning the whole sub-vector if true)