views:

232

answers:

7

Given a collection I want to iterate through all paris in a collection. Example

(all-pairs seq)

(all-pairs '(a b c d)) => ([a b] [a c] [a d] [b c] [b d] [c d]))

Here is my idea

(defn all-pairs [coll]
  (for [ [idx elmt] (indexed coll)
         other-elmt (subvec coll (inc idx))]
     (vector elmt other-elm)))

But it doesn't feel idiomatic

+4  A: 

May I suggest:

(defn all-pairs [sq] (for [i sq j sq] [i j]))

EDIT: Clearly I misread the question; since you only want distinct unduplicated pairs, we can still use this approach if a natural ordering exists on whatever domain you're calling this function on.

(defn all-pairs [sq] (filter #(< (first %) (second %)) (for [i sq j sq] [i j])))

EDIT 2

Also:

(defn all-pairs [sq]
    (partition 2 (flatten (map (fn [sqi] (map #(vector %1 %2) sq sqi))
                   (take-while not-empty (iterate rest (rest sq)))))))
Rob Lachlan
One problem is that this includes terms like [b a] and [a a], which from the description should not be included.
Ross Goddard
Indeed. I'd recommend renaming sq to coll, too.
MayDaniel
Yeah, this was my initial idea, but it includes [b a] and [a b]. I only want one.
Frank
+1  A: 

[initial attempt - overly complex]

(use '[clojure.contrib.seq :only [indexed]])

(defn all-pairs [coll]
  (for [[index i] (indexed coll) j (drop (inc index) coll)]
    [i j]))

Concerning idiom, I don't know how well I'd rank this. It could be faster too, but probably at the expense of brevity.

__

The following is much better:

(defn all-pairs [coll]
  (when-let [s (next coll)]
    (lazy-cat (for [y s] [(first coll) y])
              (all-pairs s))))

(all-pairs [1 2 3 4]) ;; => ([1 2] [1 3] [1 4] [2 3] [2 4] [3 4])

(all-pairs '(a b c d)) ;; => ([a b] [a c] [a d] [b c] [b d] [c d])

https://gist.github.com/ae0d9ebf85e9ba6e2cb3

MayDaniel
A: 

A simple recursive version that should do what you want:

(defn all-pairs [coll]
  (let [x (first coll)
        xs (rest coll)]
    (if (empty? xs) 
      nil
      (concat 
        (map (fn [y] [x y]) xs) 
        (all-pairs xs)))))
mikera
I think that you want to make your recursive call with (recur xs) rather than (all-pairs xs).
Rob Lachlan
Well, it doesn't have to be, but the stack will blow up.
Rob Lachlan
Cleaned up a bit: https://gist.github.com/ae0d9ebf85e9ba6e2cb3
MayDaniel
@Rob - surely recur won't work here as that isn't a tail recursive position?
mikera
@mikera: You're right. I'm clearly not having a good day at this reading comprehension thing. I don't see an easy way to make it tail recursive either.
Rob Lachlan
+6  A: 

How about:

(use 'clojure.contrib.combinatorics)
(vec (map vec (combinations '(a b c d) 2)))
Thomas
Clean, but *incredibly* slow.
MayDaniel
sorry, don't recognized, that the outer container is a list. So the correct version is (map vec (combinations '(a b c d) 2))
Thomas
+1 for actually using what's available.
Rob Lachlan
A: 

Not the fastest solution, but:

; handy helper function
(defn tails [v]
  "Given a sequence ( a b c ), returns all tails:  ( a b c ) ( b c ) ( c )"
  (when (seq v) 
    (lazy-cat (list v) (tails (rest v)))))

(defn pair* [v]
  "Match the first item in the list with all others in pairs."
  (when (> (count v) 1)
    (for [y v] [(first v) y])))

(defn all-pairs [v]
  (apply concat (map pair* (tails v))))
brool