Re: Get sequence of values in arbitrarily nested collection
Nice! I knew there had to be a nicer way of traversing nested collections :) Thank you for this. On Dec 6, 8:30 am, Justin Kramer wrote: > tree-seq makes this pretty simple: > > (defn nested-vals [key coll] > (for [x (tree-seq coll? seq coll) :when (contains? x key)] > (get x key))) > > This works with any type of key and all associative Clojure > structures. It could be made compatible with Java structures by > swapping out the 'coll?' predicate for something more general. > > Justin > > On Dec 5, 9:12 pm, Alex Baranosky > wrote: > > > > > > > > > Hi guys, > > > I would like a function to be able to take an arbitrarily nested collection > > and return a sequence of all values of a given key, such as :name, that > > appears anywhere in the nested collection. > > > Does anything like this already exist? > > > Thanks for the help, > > Alex -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: Get sequence of values in arbitrarily nested collection
+1 Lazy is better. Personally, I would have used filter and map instead of for, but this is probably clearer. Thanks, Alyssa On Dec 6, 10:30 am, Justin Kramer wrote: > tree-seq makes this pretty simple: > > (defn nested-vals [key coll] > (for [x (tree-seq coll? seq coll) :when (contains? x key)] > (get x key))) > > This works with any type of key and all associative Clojure > structures. It could be made compatible with Java structures by > swapping out the 'coll?' predicate for something more general. > > Justin > > On Dec 5, 9:12 pm, Alex Baranosky > wrote: > > > > > Hi guys, > > > I would like a function to be able to take an arbitrarily nested collection > > and return a sequence of all values of a given key, such as :name, that > > appears anywhere in the nested collection. > > > Does anything like this already exist? > > > Thanks for the help, > > Alex- Hide quoted text - > > - Show quoted text - -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: Get sequence of values in arbitrarily nested collection
tree-seq makes this pretty simple: (defn nested-vals [key coll] (for [x (tree-seq coll? seq coll) :when (contains? x key)] (get x key))) This works with any type of key and all associative Clojure structures. It could be made compatible with Java structures by swapping out the 'coll?' predicate for something more general. Justin On Dec 5, 9:12 pm, Alex Baranosky wrote: > Hi guys, > > I would like a function to be able to take an arbitrarily nested collection > and return a sequence of all values of a given key, such as :name, that > appears anywhere in the nested collection. > > Does anything like this already exist? > > Thanks for the help, > Alex -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: Get sequence of values in arbitrarily nested collection
On Mon, Dec 6, 2010 at 2:57 AM, Benny Tsai wrote: > When I saw the part about traversing an arbitrarily nested collection, > I immediately thought of clojure.walk (http://clojure.github.com/ > clojure/clojure.walk-api.html). I ended up with this: > > (use 'clojure.walk) > > (defn all-vals [k coll] > (let [vals (atom []) > find-val (fn [form] > (if-let [val (k form)] (swap! vals conj val)) > form)] > (prewalk find-val coll) > �...@vals)) > > user=> (all-vals :distance {:goat "al" :distance 35}) > [35] > > user=> (all-vals :distance [{:goat "al" :distance 35} > {:goat "paula" :distance 25}]) > [35 25] > > user=> (all-vals :distance [{:goat "al" :distance 35} > {:goat "paula" :distance 25 :other {:distance 99}}]) > [35 25 99] > > I wanted to use walk in a purely functional manner (instead of the > current approach of iteratively updating 'vals'). However, I was > unable to do this, given that the function passed in to prewalk needs > to preserve the structure of the nested collections. Hopefully > someone can find a way to use walk in a purely functional way here. I dunno. Seems kind of pointless, when (defn values-of [k coll] (if (instance? java.util.Map$Entry coll) (recur k [(key coll) (val coll)]) (if-let [s (try (seq coll) (catch Exception _ nil))] (let [not-found (Object.) v (if (or (associative? coll) (instance? java.util.Map coll)) (get coll k not-found) not-found) v (if-not (= v not-found) [v]) vs (map #(values-of k %) s)] (apply concat v vs) is only one line longer than what you have (counting the (use ...) and following blank line), is pure functional, and uses only clojure.core. And probably works on some of the java.util collections where the clojure.walk version probably fails. In fact, your code seems to require k to be a keyword, instead of allowing it to be a string, or a number, or even nil. -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: Get sequence of values in arbitrarily nested collection
When I saw the part about traversing an arbitrarily nested collection, I immediately thought of clojure.walk (http://clojure.github.com/ clojure/clojure.walk-api.html). I ended up with this: (use 'clojure.walk) (defn all-vals [k coll] (let [vals (atom []) find-val (fn [form] (if-let [val (k form)] (swap! vals conj val)) form)] (prewalk find-val coll) @vals)) user=> (all-vals :distance {:goat "al" :distance 35}) [35] user=> (all-vals :distance [{:goat "al" :distance 35} {:goat "paula" :distance 25}]) [35 25] user=> (all-vals :distance [{:goat "al" :distance 35} {:goat "paula" :distance 25 :other {:distance 99}}]) [35 25 99] I wanted to use walk in a purely functional manner (instead of the current approach of iteratively updating 'vals'). However, I was unable to do this, given that the function passed in to prewalk needs to preserve the structure of the nested collections. Hopefully someone can find a way to use walk in a purely functional way here. On Dec 5, 7:12 pm, Alex Baranosky wrote: > Hi guys, > > I would like a function to be able to take an arbitrarily nested collection > and return a sequence of all values of a given key, such as :name, that > appears anywhere in the nested collection. > > Does anything like this already exist? > > Thanks for the help, > Alex -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: Get sequence of values in arbitrarily nested collection
On Sun, Dec 5, 2010 at 10:03 PM, Ken Wesson wrote: > On Sun, Dec 5, 2010 at 9:12 PM, Alex Baranosky > wrote: >> Hi guys, >> I would like a function to be able to take an arbitrarily nested collection >> and return a sequence of all values of a given key, such as :name, that >> appears anywhere in the nested collection. >> Does anything like this already exist? > > It does now: > > (defn values-of [k coll] > (if-let [s (try (seq coll) (catch Exception _ nil))] > (let [not-found (Object.) > v (if (associative? coll) (get coll k not-found) not-found) > v (if-not (= v not-found) [v]) > vs (map #(values-of k %) s)] > (apply concat v vs > > user=> (values-of :k [1 2 {:k 3 :l 4} 5 #{6} [[{:k 2}] {:k {:a 3 :k 7}}]]) > (3 > 2 > {:a 3, :k 7} > 7) > > It won't quite work perfectly for java.util collections, specifically > because (associative? (java.util.HashMap.)) comes back false for some > reason. This should work even for Java collections including maps: (defn values-of [k coll] (if (instance? java.util.Map$Entry coll) (recur k [(key coll) (val coll)]) (if-let [s (try (seq coll) (catch Exception _ nil))] (let [not-found (Object.) v (if (or (associative? coll) (instance? java.util.Map coll)) (get coll k not-found) not-found) v (if-not (= v not-found) [v]) vs (map #(values-of k %) s)] (apply concat v vs) user=> (values-of :k (doto (java.util.HashMap.) (.put :k "foo"))) ("foo") user=> (values-of :k (doto (java.util.HashMap.) (.put {:k 3} "foo"))) (3) The new if at the start is because Clojure doesn't treat non-Clojure Map$Entries as equivalent to [key val] so I turn all Map$Entries into such vectors manually. As you can see, it will delve into both the keys and the values of even java.util Maps now. -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: Get sequence of values in arbitrarily nested collection
On Sun, Dec 5, 2010 at 9:12 PM, Alex Baranosky wrote: > Hi guys, > I would like a function to be able to take an arbitrarily nested collection > and return a sequence of all values of a given key, such as :name, that > appears anywhere in the nested collection. > Does anything like this already exist? It does now: (defn values-of [k coll] (if-let [s (try (seq coll) (catch Exception _ nil))] (let [not-found (Object.) v (if (associative? coll) (get coll k not-found) not-found) v (if-not (= v not-found) [v]) vs (map #(values-of k %) s)] (apply concat v vs user=> (values-of :k [1 2 {:k 3 :l 4} 5 #{6} [[{:k 2}] {:k {:a 3 :k 7}}]]) (3 2 {:a 3, :k 7} 7) It won't quite work perfectly for java.util collections, specifically because (associative? (java.util.HashMap.)) comes back false for some reason. If the only maps are Clojure maps it should work. If the key is an integer it will count vector entries at that index as values. It should work if a map *key* is itself a structure that contains the key and it should also correctly collect nils: user=> (values-of :k {{:k 3} 'foo :k nil}) (nil 3) -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: Get sequence of values in arbitrarily nested collection
The above is using Midje syntax: https://github.com/marick/Midje -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: Get sequence of values in arbitrarily nested collection
Here's my first attempt: (defn all-vals [key coll] (let [all-vals-w-key (partial all-vals key)] (cond (map? coll) (concat [(key coll)] (all-vals-w-key (vals coll))) (or (vector? coll) (seq? coll)) (apply concat (map all-vals-w-key coll) Call it like so: (fact (all-vals :distance {:goat "al" :distance 35}) => [35]) (fact (all-vals :distance [{:goat "al" :distance 35} {:goat "paula" :distance 25}]) => [35, 25]) (fact (all-vals :distance [{:goat "al" :distance 35} {:goat "paula" :distance 25 :other {:distance 99}}]) => [35, 25, 99]) -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en