Skip to content

Instantly share code, notes, and snippets.

@ericnormand

ericnormand/00 Uniques.md

Last active Jun 26, 2020
Embed
What would you like to do?

unique values

Let's keep it easy this week as we get back into it.

Write a function called uniques that takes a sequence of values. That sequence is going to have zero or more unique values. The rest will repeat at least once. Your job is to return the unique values in the same order as they appear in the argument sequence.

Examples

(uniques [1 2 3 4 5 6 1 2 3 5 6]) ;=> (4)
(uniques [:a :b :c :c]) ;=> (:a :b)
(uniques [1 2 3 1 2 3]) ;=> ()

Thanks to this site for the challenge idea where it is considered Medium level in Python.

Email submissions to eric@purelyfunctional.tv before June 21, 2020. You can discuss the submissions in the comments below.

(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn uniques
[vals]
(let [freqs (frequencies vals)
result (vec (keys (t/submap-by-vals freqs #{1} :missing-ok)))]
result))
(dotest
(is= (uniques [1 2 3 4 5 6 1 2 3 5 6]) [4])
(is= (uniques [:a :b :c :c]) [:a :b])
(is= (uniques [1 2 3 1 2 3]) []))
(defn uniques [xs]
(->> (get (group-by second (frequencies xs)) 1)
(map first)))
(ns uniques)
(require '[clojure.test :as test])
(defn uniques [coll]
(filter (fn [elt-e]
(= 1 (count
(filter (fn [elt-i]
(= elt-i elt-e))
coll))))
(reverse (set coll))))
(test/deftest uniques-test
(test/is (= '(4) (uniques [1 2 3 4 5 6 1 2 3 5 6])))
(test/is (= '(:a :b) (uniques [:a :b :c :c])))
(test/is (= '() (uniques [1 2 3 1 2 3]))))
(test/run-tests 'uniques)
(defn uniques [xs]
(->> (group-by identity xs)
(vals)
(filter (comp (partial = 1) (partial count)))
(mapcat identity)))
(ns purelyfun.382.core)
(defn uniques
[values]
(map first (filter #(= (count %) 1) (vals (group-by identity values)))))
(ns purelyfun.382.test
(:require [clojure.test :refer :all]
[purelyfun.382.core :refer :all]))
(deftest test-unique
(is (= (uniques [1 2 3 4 5 6 1 2 3 5 6]) '(4)))
(is (= (uniques [:a :b :c :c]) '(:a :b)))
(is (= (uniques [1 2 3 1 2 3]) '()))
)
(comment
(clojure.test/run-tests 'purelyfun.382.test)
(defn rt []
(let [tns 'purelyfun.382.test]
(use tns :reload-all)
(clojure.test/test-ns tns))
)
)
(defn single-in-coll? [n coll] (= 1 (count (filter #(= % n) coll))))
(defn uniques [coll] (filter #(single-in-coll? % coll) coll))
(defn ordered-frequencies [seq] (reduce #(assoc %1 %2 (inc (get %1 %2 0))) (array-map) seq))
(defn uniques [seq]
(map first
(filter
#((partial = 1) (second %))
(ordered-frequencies seq))))
(ns functional-tv-puzzles.-2020.uniques-382)
;; Hand-rolled without frequencies. Two-pass, including ordering
(defn ordering [xs]
(->> xs
(into {} (map-indexed #(vector %2 %1)))))
(defn uniques [xs]
(let [o (ordering xs)]
(loop [seen #{}
kept (sorted-set-by #(< (o %) (o %2)))
[x & more :as coll] xs]
(cond
(empty? coll)
(apply list kept)
(seen x)
(recur seen (disj kept x) more)
:else
(recur (conj seen x) (conj kept x) more)))))
(defn uniques [coll]
(sort (vec (set coll))) )
(defn uniques
"Returns a seq containing the values that appear exactly once in `s`.
The order in which the values appear in `s` is preserved."
[s]
(->> s
(filter #(= 1
((frequencies s) %)))))
;; Note to self: when ordering is involved, test with at
;; least 33 elements. Data transformation pipelines often
;; include tranforming to maps (e.g. frequencies, zipmap).
;; Coincidentally, for small maps, Clojure seems to preserve
;; order up to and including 8 elements; ClojureScript does
;; this up to and including 32 elements.
(assert (= (uniques (concat (range 33) [1 3 5]))
[0 2 4 6 7 8 9 10 11 12 13 14 15 16 17 18
19 20 21 22 23 24 25 26 27 28 29 30 31 32]))
(defn uniques [xs]
(filter (comp #(= 1 %) (frequencies xs)) xs))
(defn uniques [l]
(for [e l :when (= 1 (count (filter #{e} l)))] e))
(defn uniques [coll]
(->> coll
(group-by identity)
(filter (fn [[_ [_ & rest]]] (empty? rest)))
(map first)))
(defn uniques [coll]
(let [freqs (frequencies coll)]
(filter #(= 1 (get freqs %)) coll)))
(defn uniques
[xs]
(let [unique-items (->> xs
frequencies
(filter #(= 1 (val %)))
keys
set)]
(if (some nil? unique-items)
(filter #(or (nil? %)
(unique-items %)) xs )
(filter unique-items xs ))))
(deftest uniques-test
(testing "uniques"
(is (= '(:a :b) (uniques [:a :b :c :c])))
(is (= '(4) (uniques [1 2 3 4 5 6 1 2 3 5 6])))
(is (= '() (uniques [1 2 3 1 2 3])))
(is (= '(nil 1) (uniques [nil 2 3 1 2 3])))
(is (= '() (uniques [nil nil ])))))
(defn uniques [xs]
(let [counts (frequencies xs)
dups (->> (filter (fn [[x cnt]] (> cnt 1)) counts)
(map first)
set)]
(remove #(contains? dups %) xs)))
(defn uniques
"Given a sequence of values return, in the same order in which they
appear, those among them that are unique e.g. [1 2 1 3] ==> [2 3]."
[v]
(let [same (group-by identity v)]
(reduce (fn [res k]
(if (= 1 (count (get same k)))
(conj res k)
res))
[]
v)))
(defn uniques [xs]
(let [freqs (frequencies xs)]
(filter #(= 1 (freqs %)) xs)))
(defn uniques [xs]
(let [freq (frequencies xs)]
(for [x xs
:when (= 1 (get freq x))]
x)))
@souenzzo

This comment has been minimized.

Copy link

@souenzzo souenzzo commented Jun 15, 2020

(defn uniques
  [coll]
  ;; To return if a element is unique, I need
  ;; to realize all collection. So `frequencies`
  ;; should do that in a optimal way
  (let [h (frequencies coll)]
    ;; I can return result lazily, once I have a "global index"
    (filter (comp #{1} h)
            coll)))
@NPException

This comment has been minimized.

Copy link

@NPException NPException commented Jun 15, 2020

I just noticed that @souenzzo posted essentially same solution already, but here is mine anyway:

(defn uniques
  [xs]
  (let [counts (frequencies xs)]
    (filter #(== 1 (counts %)) xs)))
@KingCode

This comment has been minimized.

Copy link

@KingCode KingCode commented Jun 15, 2020

I am inspired by @souenzzo's (comp #{1} ...). My solution is more convoluted by iterating on the frequencies instead of the coll. Two variants:

(defn ordering [xs]
  (->> xs (map-indexed #(vector %2 %))
       (into {})))

(defn key-when [pred]
  (fn [[k v]]
    (when (pred v)
      k)))

(defn uniques [xs]
  (->> xs 
       frequencies
       (keep (key-when #{1}))
       (sort-by (ordering xs))))

(defn uniques-2 [xs]
  (let [o (ordering xs)]
    (->> xs
         frequencies
         seq flatten
         (apply sorted-map-by #(compare (o %) (o %2)))
         (keep (key-when #{1})))))
@burnall

This comment has been minimized.

Copy link

@burnall burnall commented Jun 15, 2020

It is a rare case when it is optimal to use -> pipeline with collections.

(defn uniques [coll]
  (-> coll
      (frequencies)
      (#(fn [x] (= 1 (% x))))
      (filter coll)))
@duzunov

This comment has been minimized.

Copy link

@duzunov duzunov commented Jun 15, 2020

Here is my submission:

(defn single-in-coll? [n coll]
  (= 1 (count (filter #(= % n) coll)))) 
(defn uniques [coll]
  (filter #(single-in-coll? % coll) coll))
@zugnush

This comment has been minimized.

Copy link

@zugnush zugnush commented Jun 16, 2020

I think from the spec that maybe a unique nil should be reported

(defn uniques
  [xs]
  (let [unique-items (->> xs
                          frequencies
                          (filter #(= 1 (val %)))
                          keys
                          set)]
    (if (some nil? unique-items)
      (filter #(or (nil? %)
                   (unique-items %)) xs )
      (filter unique-items xs ))))

(deftest uniques-test
  (testing "uniques"
    (is (= '(:a :b) (uniques [:a :b :c :c])))
    (is (= '(4) (uniques [1 2 3 4 5 6 1 2 3 5 6])))
    (is (= '() (uniques [1 2 3 1 2 3])))
    (is (= '(nil 1) (uniques [nil 2 3 1 2 3])))
    (is (= '() (uniques [nil nil ])))))
@tychobrailleur

This comment has been minimized.

Copy link

@tychobrailleur tychobrailleur commented Jun 17, 2020

(defn uniques [v]
  (->> v
       (frequencies)
       (filter #(<= (second %) 1))
       (map first)))
@burnall

This comment has been minimized.

Copy link

@burnall burnall commented Jun 18, 2020

@tychobrailleur This condition "unique values in the same order as they appear in the argument sequence" is not fulfilled, unfortunately. It would be nice to use into for java.util.LinkedHashSet which keeps insertion order, but it does not work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.