0
votes

I do not think the key-pres? function below is working the way I expect it to. First, here is the input data.

Data from which cmp-val derived: ["2" "000-00-0000" "TOAST" "FRENCH" "" "M" "26-Aug-99" "" "ALL CARE PLAN" "MEDICAL"] Data that is missing the key (ssn). ["000-00-0000" "TOAST " "FRENCH " "RE-PART B - INSURED "]

The problem is, if I make one of the input's 000-00-0000 something else, I should see that conj'd onto a log vector. I don't, and I don't see it printed with the if-not empty?.

(defn is-a-in-b
    "This is a helper function that takes a value, a column index, and a 
     returned clojure-csv row (vector), and checks to see if that value
     is present. Returns value or nil if not present."

    [cmp-val col-idx csv-row]

    (let [csv-row-val (nth csv-row col-idx nil)]
        (if (= cmp-val csv-row-val)
            cmp-val
            nil)))

(defn key-pres?
    "Accepts a value, like an index, and output from clojure-csv, and looks
     to see if the value is in the sequence at the index. Given clojure-csv
     returns a vector of vectors, will loop around until and if the value
     is found."

    [cmp-val cmp-idx csv-data]

    (let [ret-rc 
        (for [csv-row csv-data
                :let [rc (is-a-in-b cmp-val cmp-idx csv-row)]
                :when (true? (is-a-in-b cmp-val cmp-idx csv-row))]
            rc)]
        (vec ret-rc)))

(defn test-key-inclusion
    "Accepts csv-data file and an index, a second csv-data param and an index,
     and searches the second csv-data instances' rows (at index) to see if
     the first file's data is located in the second csv-data instance."

    [csv-data1 pkey-idx1 csv-data2 pkey-idx2]

    (reduce
        (fn [out-log csv-row1]
            (let [cmp-val (nth csv-row1 pkey-idx1 nil)]
                (doseq [csv-row2 csv-data2]
                  (let [temp-rc (key-pres? cmp-val pkey-idx2 csv-row2)]
                      (if-not (empty? temp-rc) 
                          (println cmp-val, " ", (nth csv-row2 pkey-idx2 nil), " ", temp-rc))
                      (if (nil? temp-rc)
                        (conj out-log cmp-val))))))
         []
         csv-data1))

What I want the function to do is traverse data returned by clojure-csv (a vector of vectors). If cmp-val can be found at the cmp-idx location in csv-row, I'd like that assigned to rc, and the loop to terminate.

How can I fix the for loop, and if not, what looping mechanism can I use to accomplish this?

Thank you.

1

1 Answers

1
votes
  • you don't need true?, it specifically checks for the boolean true value;
  • don't repeat the call to is-a-in-b;
  • it would be more idiomatic (and readable) to use a-in-b? as the fn name;
  • I suggest simplifying the code, you don't really need that let.

Code:

(vec (for [csv-row csv-data
           :let [rc (a-in-b? cmp-val cmp-idx csv-row)]
           :when rc)]
        rc))

But, this are just some general comments on code style... what you're implementing here is just a simple filter:

(vec (filter #(a-in-b? cmp-val cmp-idx %) csv-data))

Furthermore, this will return not only the first, but all matches. If I read your question right, you just need to find the first match? Then use some:

(some #(a-in-b? cmp-val cmp-idx %) csv-data)

UPDATE

Rereading your question I get the feeling that you consider for to be a loop construct. It's not -- it's a list comprehension, producing a lazy seq. To write a loop where you control when to iterate, you must use loop-recur. But in Clojure you'll almost never need to write you own loops, except for performance. In all other cases you compose higher-order functions from clojure.core.