Re: Get sequence of values in arbitrarily nested collection

2010-12-06 Thread Benny Tsai
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

2010-12-06 Thread Alyssa Kwan
+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

2010-12-06 Thread Justin Kramer
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

2010-12-06 Thread Ken Wesson
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

2010-12-05 Thread Benny Tsai
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

2010-12-05 Thread Ken Wesson
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

2010-12-05 Thread Ken Wesson
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

2010-12-05 Thread Alex Baranosky
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

2010-12-05 Thread Alex Baranosky
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