How to iterate through all alphabet letters in ClojureScript?

In languages with an integer-based Char datatype, such as C and Java and probably by inheritance JVM Clojure, you could iterate through characters by the equivalent of (+ \A 1). JS doesn’t have chars though; only strings. Is there a nice way out-of-the-box to iterate through the characters of the alphabet, perhaps just in one case? I mean, obviously we could create a collection that contains all the letters we want, but is that already done somewhere?

1 Like

With a little bit of interop

(map String/fromCodePoint
  (range (.codePointAt \a 0) 
         (.codePointAt \z 0)))
5 Likes

A bit off topic maybe, but why does String not resolve in CLJS to something, but String/fromCodePoint does resolve to something? Both js/String and js/String.fromCodePoint do work (as expected).

2 Likes

An impractical, yet fun solution that also works in Clojure :grinning:

(-> (into (sorted-set) (map char) (range 0xFF))
   (subseq <= \A <= \Z))
5 Likes

That is a good question. I’m guessing it’s the same reason that (Math/floor ...) works.

This returns the same result as the solution, but with no need for interop.

(map char (range 97 122)) ; Lower case letters
(map char (range 65 91)) ; Upper case letters
3 Likes

Fascinating! I would argue that this is one case that the interop version is acceptable, for two reasons: First, Clojure[Script] is a hosted language, so a modest recognition of that fact isn’t wrong. Second, having the line like (.codePointAt \a 0) is self-describing, since I can better understand \a than 97.

On the flip side, there is a definite advantage to your code being able to work in CLJC. In that case I would simply wrap it like

(let [letter-a 97
       letter-z 122]
(map char (range letter-a letter-z))

Thanks for the solution!

I would usually use a name for the intended output of the code (its value), e.g.

(def lower-case-letters (map char (range 97 122)))

That way you can further use lower-case-letters as you please.

Note that if you wanted to ensure that both Clojure and ClojureScript code had the same output, you could use:

(def lower-case-letters (map (comp str char) (range 97 122)))

This will give you a list of "a", "b", "c", etc. in Clojure as well, instead of \a,\b,\c, etc.

3 Likes

excellent points – there should be some wisdom post on readable refactoring and which things you make into vars. I like your lower-case-letters better than my letter-a

I’m sure this is in a book somewhere, possibly @ztellman Elements of Clojure, but it hasn’t hit my consciousness like this before

1 Like

My first thought is why? Often when people ask the question they are really asking about ASCII7/ISO Latin-1 1252. here are lots of alphabetic characters that aren’t in the list. There are hundreds of other Latin characters, more added every few years. There there are non-Latin characters, syllabaries, ideographs, and special characters like ∞. Are you looking to remove white space, separators, & control characters? Non-printable characters? —- Depending on the answer, the best technique is very different. Things like this are not built into JavaScript because it generally falls into the “worst practices” bin. — If it’s your authorization/authentication system, send the entire string back to the system for validation rather than doing it yourself. Subsystems should usually do their own validation as rules can change over time. If it’s a person’s name, even email address, validation rules should be based on the national language, not ASCII.

Fair question! in short, with my student team we are making a toy Wordle game with ClojureScript, and iterating through the letters manually seemed silly, especially as we are exploring.

Incidentally, we found exactly what inspired borkdude comment about the strangeness of String/fromCodePoint, which gets flagged as bad syntax by Kondo precisely because it’s unclear what String actually is. We couldn’t figure out what should be :import to please Kondo! But it works, nonetheless, which is bizarre.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.