From nested values to sequences

I did this for two arguments, but now would like to generalize to any number of arguments.

Considering:

(def m1 {"k1" {"email" "oad", "env" "prod" "inf" "eth0"}, "k2" {"email" "cmi", "env" "prod" "inf" "eth0"} "k3" {"email" "oad", "env" "prod" "inf" "eth0"} "k4" {"email" "oad", "env" "prod" "inf" "eth0"} "k5" {"email" "cmi", "env" "prod" "inf" "eth0"}})

The intended result will be to have a map having the keys as a kind of the primary key and the value as the keys on m1, like this:

[{:oad :prod :eth0} "k1", {:cmi :prod :eth0} "k2" ....]

This function create a sequence of keywords for all values of some attribute.

(def m1 {"k1" {"email" "oad", "env" "prod" "inf" "eth0"}, "k2" {"email" "cmi", "env" "prod" "inf" "eth0"} "k3" {"email" "oad", "env" "prod" "inf" "eth0"} "k4" {"email"   "oad", "env" "prod" "inf" "eth0"} "k5" {"email" "cmi", "env" "prod" "inf" "eth0"}})
  (defn keyw-of-val
    [m v]
    (let [ vals-m (map keyword (map (fn [x] (get (val x) v)) m) ) ]
    (map keyword vals-m)
    ))

So that invoking it:

 (keyw-of-val m1 "email")
; (:oad :cmi :oad :oad :cmi)

But all my attemps to create a map of keyw-of-val to all attributes wanted ["email" "env" "inf"] were unsuccessful, like:

(into [] (map ( #( keyw-of-val m1 % ) ["email" "env" "inf"]) ["email" "env" "inf"]))

The problem I’m facing is that the contents of the vector of attributes has to be passed twice.
Tried also partial and juxt but don’t seem like the right tools for this.

Once there’s a vector of vectors like
[[:oad :cmi :oad :oad :cmi] [:prod :prod :prod :prod :prod] [:eth0 :eth0 :eth0 :eth0 :eth0]
it’ll be possible to use interleave and group-by to achieve the desired output.

1 Like

Not 100% certain what you want because the provided desired data structure is not valid (map forms with an uneven amount of items). But maybe this?

(let [data {"k1" {"email" "oad", "env" "prod" "inf" "eth0"}
            "k2" {"email" "cmi", "env" "prod" "inf" "eth0"}
            "k3" {"email" "oad", "env" "prod" "inf" "eth0"}
            "k4" {"email" "oad", "env" "prod" "inf" "eth0"}
            "k5" {"email" "cmi", "env" "prod" "inf" "eth0"}}
      ks ["email" "env" "inf"]]
  (into {}
        (map (fn [[k mv]]
               [(into #{} (map #(keyword (mv %))) ks)
                k]))
        data))
=> {#{:prod :eth0 :oad} "k4", #{:prod :eth0 :cmi} "k5"}
2 Likes

Thank you @p-himik, it’s about that!
I have yet to study your solution :slight_smile:
All the best.

I’m having some difficulty in understanding the function you provided.

There’s a (fn [ ..... ] [ ........ ] that’s two pairs of square brackets.
I tried to find out for this in the documentation and couldn’t find anything.
Following (fn [ ..... ] I’d expect something to be evaluated inside ( ..... ).

So, what is (fn [ ... ] [ ... ]) instead of (fn [ ... ] ( .......)), can you please explain?

So, the (fn [...] ...) form, when evaluated, creates a function. The first ... are the arguments (if you include the brackets, it’s called an “arguments vector”) and the second ... are the body of the function - what it runs, when executed, and what it returns.

In the (fn [...] [...]) form, the body is [...], so the body is a vector. The function calculates whatever has to be put in the vector, puts it all in the vector in the specified order, and returns that vector.

In the specific case above, the body vector is a 2-item vector, or a 2-tuple (sometimes just “tuple”), which can be used wherever a map entry is expected. A map entry is a key-value pair that you can conj onto a map, making it effectively the same as assoc.

2 Likes

Great, thank you!
I should have seen that!

Clojure and Lisps are much more free form languages, good for creativity.
Other languages, including all main stream, serve to narrow the mind of everyone.