0

(test [1 2 3]) is returning [], not [1 2 3], why?

(defn test [some-array]
      (let [x []]
        (doseq [i some-array]
          (conj x i))
        x))

(test [1 2 3])

EDIT: a comment below suggested I am trying an imperative approach with a functional language, and that's probably correct. What I am trying to do is build up a vector of vectors using an initial sequence of values (inside a vector in this case) as starting data. I guess I could try to transform each value in the vector and return it, but that seems harder conceptually (from an imperative mindset) than taking a starting vector and using it to build out a new one and return that.

As an example, suppose I had a function that takes 1 and returns "one", and I wanted to have a function take [1 2 3] and return [[1 "one"][2 "two"][3 "three"]].

3
  • I just realized I am confusing the ideas of arrays and vectors... not sure that is part of the problem here. Commented Mar 31, 2021 at 19:24
  • Also, tried for instead of doseq with the same result... curious about the difference between those two, though. Commented Mar 31, 2021 at 19:26
  • It's very hard to guess (at least for me), what you are actually after. Could you please be specific, what transformation is in your mind? Do you want to map each item of some-var or do you want to shrink/increase the items etc? Commented Mar 31, 2021 at 19:39

4 Answers 4

3

Clojure's focus is on immutable data and functional programming. Your code there feels like you want to do some imperative style ideas here.

Your test there could as well be identity - a copy of some immutable data structure, is just the immutable data structure itself.

If your example there is oversimplified and you want to "append" to some x with data inside it, you could use into.

edit

To transform a sequence one-by-one (e.g. each input results in a different output), use map (or mapv to get a vector and make it eager). E.g.

(def numbers
  {1 "one"
   2 "two"
   3 "three"})

(defn test [some-array]
  (mapv #(vector % (numbers %)) some-array))

(println (test [1 2 3]))
; → [[1 one] [2 two] [3 three]]

(Bonus points for using juxt as mapping function)

Sign up to request clarification or add additional context in comments.

5 Comments

This is what I was sensing... I seem to be recreating a JS scenario. Not that it couldn't be done 'right' in JS... I just mean the typical JS approach.
I think you want to copy some-array, but there is no apparent need for that. You still could use into to create a new container, that holds all items of some-array.
I added an edit to my original post to clarify things (or maybe confuse things!).
Your suggestion (via your edit) is so succinct! Wow.
Also, intermediate variables are really the thing that has gotten so tired for me... so very happy to move away from let when I can.
2

As others have pointed out, some ways of building the result are easier than others. In this case, we see that each element of the result vector is computed from a corresponding element in the input vector. This is a good indication to use map (or mapv):

(def nums ["zero" "one" "two" "three" "four" "five"])

(defn arr-test [xs]
  (mapv (juxt identity nums) xs))

Comments

1

there is also a handy util clojure.pprint/cl-format in core library to get string representation of numbers (and much more!):

user> (def num-str (partial clojure.pprint/cl-format nil "~R"))
#'user/num-str

user> (mapv (juxt identity num-str) [0 1 2 234 2455323 1541524152352356235625])
;;=> [[0 "zero"]
;;    [1 "one"]
;;    [2 "two"]
;;    [234 "two hundred thirty-four"]
;;    [2455323
;;     "two million, four hundred fifty-five thousand, three hundred twenty-three"]
;;    [1541524152352356235625N
;;     "one sextillion, five hundred forty-one quintillion, five hundred twenty-four quadrillion, one hundred fifty-two trillion, three hundred fifty-two billion, three hundred fifty-six million, two hundred thirty-five thousand, six hundred twenty-five"]]

1 Comment

Whoa. I've never seen that before.
1

You really don't want to do it that way - but if you did :-) you'd want to use loop instead of let:

(defn arr-test [some-array]
  (let [digit-words {1 "one", 2 "two",   3 "three", 4 "four", 5 "five",
                     6 "six", 7 "seven", 8 "eight", 9 "nine", 0 "zero" }]
    (loop [x []
           a some-array]
      (if (empty? a)
        x
        (recur (conj x [(first a) (digit-words (first a))]) (rest a))))))

A better way would be:

(defn arr-test [some-array]
  (let [digit-words {1 "one", 2 "two",   3 "three", 4 "four", 5 "five",
                     6 "six", 7 "seven", 8 "eight", 9 "nine", 0 "zero" }]
    (mapv #(vector % (digit-words %)) some-array)))

2 Comments

This is handy to know... however, it looks like I should take the functional approach. I also just read that I should avoid arrays as opposed to vectors unless I need something specific from the Java ecosystem. Thanks for this!
The "recur loop" solution is what I worked out first when I was starting to learn Clojure, so I think of it as "beginning Clojure". "It's Clojure, Jim - but not as we know it!". :-) The second is the "better way" - using functions to do the work, which is also clearer (once you know what all those dang functions do :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.