An Architect's View

CFML, Clojure, Software Design, Frameworks and more...

An Architect's View

Fun with Zodiac signs

November 22, 2010 ·

Here's a little puzzler for you! Given the twelve signs of the zodiac, write some code to print out a list of all the valid keys / pairs such that:

  • The keys have the form: zodiac.firstsign.secondsign (all lowercase)
  • The values have the form Firstsign and Secondsign (correct case on the sign names)
  • firstsign and secondsign are in alphabetical order
  • There are no duplicates

The result will have lines like:

  • [zodiac.aquarius.aquarius Aquarius and Aquarius]
  • [zodiac.aquarius.pisces Aquarius and Pisces]
  • ...
  • [zodiac.aries.pisces Aries and Pisces]
  • [zodiac.aries.aries Aries and Aries]
  • [zodiac.aries.taurus Aries and Taurus]
  • ...

Here's the solution I came up with in Clojure:

(def z ["Aquarius" "Pisces" "Aries" "Taurus" "Gemini" "Cancer" "Leo" "Virgo" "Libra" "Scorpio" "Sagittarius" "Capricorn"])
(defn zkey[a b] [(str "zodiac." (.toLowerCase a) "." (.toLowerCase b)) (str a " and " b)])
(defn accept[a b] (<= (.compareTo a b) 0))
(defn zkeys[s] (map (partial zkey s) (filter (partial accept s) z)))
(map println (mapcat zkeys z))

The zkey function creates the key / value strings from a pair of signs. The accept function is a predicate that returns true if and only if sign a is alphabetically no greater than sign b. The zkeys function returns the set of key / value pairs for a given sign (against the full list of signs). Finally the last line applies zkeys across the full list of signs and prints the result - mapcat just flattens the results one level to make it easier to read.

If you think you have a better solution (in any language), feel free to post it as a comment!

Tags: clojure · coldfusion · programming

7 responses

  • 1 Marlon // Nov 22, 2010 at 3:46 PM

    not better, just different :)

    z = %w(Aquarius Pisces Aries Taurus Gemini Cancer Leo Virgo Libra Scorpio Sagittarius Capricorn) {|sign| {|sign2| [sign, sign2].sort.join(".")}}.flatten.uniq.each {|combo| puts "zodiac.#{combo.downcase} #{combo.gsub('.',' and ')}"}
  • 2 hsanjeewa // Nov 22, 2010 at 4:12 PM

    (use '[clojure.string :only (lower-case)])

    (doseq [[fst snd] (for [z1 z z2 z :when (>= 0 (compare z1 z2))] [z1 z2])]
        (println (lower-case (str "zodiac." fst "." snd)) fst " and " snd))
  • 3 Barney Boisvert // Nov 22, 2010 at 4:26 PM

    Here's some Groovy:

    z = ['Aquarius', 'Pisces', 'Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo', 'Libra', 'Scorpio', 'Sagittarius', 'Capricorn']
    map = [:]
    z.sort().each { a ->
       z.sort().findAll {
          it >= a
       }.each { b ->
          map["zodiac.${a.toLowerCase()}.${b.toLowerCase()}"] = "$a and $b"
    println map
  • 4 brandon // Nov 22, 2010 at 5:08 PM

    how about this:

    (defn combos []
    (for [x z
    y z
    :when (accept x y)]
    (zkey x y)))

    (clojure.pprint/pprint (combos))
  • 5 Michael Kohl // Nov 23, 2010 at 2:43 AM


    z = %w(Aquarius Pisces Aries Taurus Gemini Cancer Leo Virgo Libra Scorpio Sagittarius Capricorn)

    z.combination(2).each { |combo| p ["zodiac.#{combo.join('.').downcase} #{combo[0].capitalize} and #{combo[1].capitalize}"] }
  • 6 brandon // Nov 23, 2010 at 4:42 AM

    comment moderation makes me look like a plagiarizer.

    : )
  • 7 Sean Corfield // Nov 23, 2010 at 10:19 PM

    @brandon, yeah, I got four responses very quickly after I posted it, before I got time to approve them! I keep forgetting about 'for' in Clojure... it would make several of my functions much cleaner!