How do I sample random subsequences in Clojure's test.check? - clojure

I'm trying to generate a random solvable instance of the Subset Sum Problem. Wikipedia states that the target value should always be zero, but it's also possible to specify the target value, which is what I'm doing here.
So the idea is to create a random vector using (gen/vector gen/int) and then sample a random sub-vector and sum up that vector to create the target value. The problem with the obvious strategy using gen/elements is that it may sample the same element repeatedly.
My next best idea is to create a random set of indices and extract all the elements at those indices. Is there a simpler approach?

This generator in test.chuck does what you're asking for by generating inclusion flags for each element.

This code is a mock-up of what I think you are trying to do. It generates a vector of integers, each of which has a flag to indicate if it belongs in the subset (it could be none, some, or all). A dummy function (standin for subset-sum) decides if the test passes. You will need [tupelo "0.9.56"] in your project.clj.
(ns tst.demo.core
(:require
[clojure.test.check :as tc]
[clojure.test.check.generators :as tcgen]
[clojure.test.check.properties :as tcprop]
[tupelo.core :as t]
[tupelo.test :as tt]
[schema.core :as s]))
(def IntBoolTuple [ (s/one s/Int "x1") (s/one s/Bool "x2") ])
(s/defn is-sum-tuple? :- s/Bool
[tuple :- IntBoolTuple]
(let [[int-val bool-val] tuple]
bool-val))
(s/defn tst-fn :- s/Bool
"Dummy test function"
[tuples :- [IntBoolTuple]]
(let [all-ints (mapv first tuples)
flags (mapv second tuples)
tuples-to-add (vec (filter is-sum-tuple? tuples))
ints-to-add (mapv first tuples-to-add)
int-sum (apply + ints-to-add)]
(< -20 int-sum))) ; dummy test: pass if not too negative
(tt/dospec 999
(tcprop/for-all [tuples (tcgen/such-that
(fn st-fn [tuples] (pos? (count tuples))) ; ensure at least 1 tuple is present
(tcgen/vector ; 0 or more tuples, ex: [ [5 false] [2 true] ...]
(tcgen/tuple tcgen/int tcgen/boolean) ; a tuple like [5 false]
))]
(t/spyx tuples) ; display
(tst-fn tuples)))
The above code generates output like this:
---------------------------------------
Clojure 1.9.0-beta1 Java 9.0.1
---------------------------------------
Testing tst.demo.core
tuples => [[-1 false]]
tuples => [[1 true]]
tuples => [[-2 true] [-1 false]]
tuples => [[3 true] [-1 true]]
tuples => [[1 true]]
tuples => [[-1 true] [5 true] [-4 true] [5 false]]
tuples => [[5 true] [2 false] [3 false] [1 true] [0 true]]
tuples => [[2 false] [-4 false]]
tuples => [[2 false]]
tuples => [[2 false] [8 true] [9 true] [9 true] [3 true] [-7 true] [-9 true] [8 false] [-9 true]]
tuples => [[-9 true] [-1 true]]
tuples => [[4 false] [-6 true] [0 false] [10 true]]
tuples => [[-2 false] [7 true] [-12 false] [4 false] [4 false] [11 true] [6 false] [-5 false]]
tuples => [[-5 false] [6 true] [9 true] [-7 true] [1 false] [3 false] [-9 true] [-9 true] [-8 true] [-8 false] [-12 false]]
tuples => [[-7 false] [-14 false] [8 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-14 false] [8 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-7 false] [8 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-7 false] [-14 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-7 false] [-14 false] [8 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-7 false] [-14 false] [8 false] [-1 false] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-7 false] [-14 false] [8 false] [-1 false] [-14 true] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-7 false] [-14 false] [8 false] [-1 false] [-14 true] [-10 false] [1 false] [5 false] [-13 true]]
tuples => [[-7 false] [-14 false] [8 false] [-1 false] [-14 true] [-10 false] [-8 true] [5 false] [-13 true]]
tuples => [[-7 false] [-14 false] [8 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [-13 true]]
tuples => [[-7 false] [-14 false] [8 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false]]
tuples => [[8 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-14 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-14 false] [8 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-14 false] [8 false] [-1 false] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-14 false] [8 false] [-1 false] [-14 true] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-14 false] [8 false] [-1 false] [-14 true] [-10 false] [1 false] [5 false] [-13 true]]
tuples => [[-14 false] [8 false] [-1 false] [-14 true] [-10 false] [-8 true] [5 false] [-13 true]]
tuples => [[-14 false] [8 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [-13 true]]
tuples => [[-14 false] [8 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false]]
tuples => [[-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[8 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[8 false] [-1 false] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[8 false] [-1 false] [-14 true] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[8 false] [-1 false] [-14 true] [-10 false] [1 false] [5 false] [-13 true]]
tuples => [[8 false] [-1 false] [-14 true] [-10 false] [-8 true] [5 false] [-13 true]]
tuples => [[8 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [-13 true]]
tuples => [[8 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false]]
tuples => [[-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-1 false] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-1 false] [-14 true] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-1 false] [-14 true] [-10 false] [1 false] [5 false] [-13 true]]
tuples => [[-1 false] [-14 true] [-10 false] [-8 true] [5 false] [-13 true]]
tuples => [[-1 false] [-14 true] [-10 false] [-8 true] [1 false] [-13 true]]
tuples => [[-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false]]
tuples => [[-10 false] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-14 true] [-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-14 true] [-10 false] [1 false] [5 false] [-13 true]]
tuples => [[-14 true] [-10 false] [-8 true] [5 false] [-13 true]]
tuples => [[-14 true] [-10 false] [-8 true] [1 false] [-13 true]]
tuples => [[-14 true] [-10 false] [-8 true] [1 false] [5 false]]
tuples => [[-8 true] [1 false] [5 false] [-13 true]]
tuples => [[-10 false] [1 false] [5 false] [-13 true]]
tuples => [[-10 false] [-8 true] [5 false] [-13 true]]
tuples => [[-10 false] [-8 true] [1 false] [-13 true]]
tuples => [[-10 false] [-8 true] [1 false] [5 false]]
tuples => [[1 false] [5 false] [-13 true]]
tuples => [[-8 true] [5 false] [-13 true]]
tuples => [[-8 true] [1 false] [-13 true]]
tuples => [[-8 true] [1 false] [5 false]]
tuples => [[5 false] [-13 true]]
tuples => [[-8 true] [-13 true]]
tuples => [[-8 true] [5 false]]
tuples => [[-13 true]]
tuples => [[-8 true]]
tuples => [[0 true] [-13 true]]
tuples => [[-4 true] [-13 true]]
tuples => [[-6 true] [-13 true]]
tuples => [[-7 true] [-13 true]]
tuples => [[-8 false] [-13 true]]
tuples => [[-13 true]]
tuples => [[-7 true]]
tuples => [[0 true] [-13 true]]
tuples => [[-4 true] [-13 true]]
tuples => [[-6 true] [-13 true]]
tuples => [[-7 false] [-13 true]]
tuples => [[-7 true] [0 true]]
tuples => [[-7 true] [-7 true]]
tuples => [[-7 true] [-10 true]]
tuples => [[-7 true] [-12 true]]
tuples => [[-7 true] [-13 false]]
{:result false, :seed 1511919758351, :failing-size 14, :num-tests 15, :fail [[[-7 false] [-14 false] [8 false] [-1 false] [-14 true] [-10 false] [-8 true] [1 false] [5 false] [-13 true]]], :shrunk {:total-nodes-visited 27, :depth 9, :result false, :smallest [[[-7 true] [-13 true]]]}, :test-var "dospec-line-46"}
FAIL in (dospec-line-46) (clojure_test.cljc:21)
expected: result
actual: false

Related

How to pretty print a Clojure data structure in HTML?

How to make the following code generate a similar visualization to pprint in HTML?
(let [clj-structure {:ui {:selected #{[:A 3]}}
:domain
{:C {0 {:value 12}}
:D {0 {:dependants [[:C 0]] :string "3" :value "3"}
1 {:dependants [[:C 0]] :string "4" :value "4"}}
:E {2 {:dependants [] :string "2" :value "2"}
3 {:dependants [] :string "10" :value "10"}}}}]
[:p (str clj-structure)])
(let [clj-structure {:ui {:selected #{[:A 3]}}
:domain
{:C {0 {:value 12}}
:D {0 {:dependants [[:C 0]] :string "3" :value "3"}
1 {:dependants [[:C 0]] :string "4" :value "4"}}
:E {2 {:dependants [] :string "2" :value "2"}
3 {:dependants [] :string "10" :value "10"}}}}]
[:pre (with-out-str (cljs.pprint/pprint clj-structure))])

Reagent atom swap! change state to all values from keys

We have an atom with some keys and boolean values:
(def btns (reagent.core/atom {:a true :b true :c true}))
I need to change the state of all keys, like this: {:a false :b false :c false}
I tried this, but not good solution, and not working:
(for [btn #btns]
(swap! btns assoc (key btn) false))
you can update multiple keys this way:
user> (def a (atom {:a true :b true :c true}))
;;=> #<Atom#af0fa6a: {:a true, :b true, :c true}>
user> (swap! a into {:b false :c false})
;;=> {:a true, :b false, :c false}
or like this:
user> (swap! a assoc :a false :c false)
;;=> {:a false, :b true, :c false}
if you want to update all the keys in atom to false it could also look like this:
user> (reset! a (zipmap (keys #a) (repeat false)))
;;=> {:a false, :b false, :c false}
or like this:
user> (swap! a #(zipmap (keys %) (repeat false)))
;;=> {:a false, :b false, :c false}
update
also, it is good to abstract out the util function, to make it more readable:
(defn assoc-all-keys [data val]
(zipmap (keys data) (repeat val)))
user> (swap! a assoc-all-keys false)
;;=> {:a false, :b false, :c false}

How to use assoc-in to update multiple values in a map? (Clojure)

I am trying to update every line that has an "enjoy-clojure?" that returns true "sanity-rating" to -2 (i.e johnny's sanity-rating would be updated to -2)
(def student-database
{ 0 {:enjoy-clojure? false, :name "jimmy",:sanity-rating 9}
1 { :enjoy-clojure? true, :name "johnny",:sanity-rating 2}
2 { :enjoy-clojure? true, :name "jilly",:sanity-rating 5}
3 { :enjoy-clojure? true, :name "janey",:sanity-rating 8}
4 {:enjoy-clojure? false, :name "jelly",:sanity-rating 10}})
I am new to Clojure and have tried researching update and assoc and can't really seem to find a way to update multiple elements ((assoc student-database [0 :sanity-rating] -2) only returns updates one element). To filter student-database to take out the student's who returned true I have
(defn unhinged?
[record]
(:enjoy-clojure? record))
(defn minus-two-students
[student-database]
(filter #(unhinged? %)
(map student-database [0 1 2 3 4])))
And returns
({:enjoy-clojure? true, :name "johnny", :sanity-rating 2} {:enjoy-clojure?
true, :name "jilly", :sanity-rating 5} {:enjoy-clojure? true, :name
"janey", :sanity-rating 8})
Which works great but I also need it to update all their sanity-rating to -2. Any helps/tips would be much appreciated.
Here comes the reduce-kv version!
(defn adjust-sanity [student]
(if (:enjoy-clojure? student)
(assoc student :sanity-rating -2)
student))
(reduce-kv (fn [m k v] (assoc m k (adjust-sanity v)))
{}
student-database)
=>
{0 {:enjoy-clojure? false, :name "jimmy", :sanity-rating 9},
1 {:enjoy-clojure? true, :name "johnny", :sanity-rating -2},
2 {:enjoy-clojure? true, :name "jilly", :sanity-rating -2},
3 {:enjoy-clojure? true, :name "janey", :sanity-rating -2},
4 {:enjoy-clojure? false, :name "jelly", :sanity-rating 10}}
Or another option with a helper function to update all a map's values:
(defn update-vals [m f]
(reduce-kv (fn [m' k v] (assoc m' k (f v))) {} m))
(update-vals student-database adjust-sanity)
You didn't say in your question that you want the function to returns only (= enjoy-clojure? true) records, but from your comments in the other answers, I feel that's what you really want.
So maybe this?
(defn unhinged?
[record]
(:enjoy-clojure? record))
(defn minus-two-students
[student-database]
(->> student-database
vals
(filter unhinged?)
(map #(assoc % :sanity-rating -2))))
Output will be
({:enjoy-clojure? true, :name "johnny", :sanity-rating -2}
{:enjoy-clojure? true, :name "jilly", :sanity-rating -2}
{:enjoy-clojure? true, :name "janey", :sanity-rating -2})
To update the entire db, you could do:
(def student-database
{0 {:enjoy-clojure? false, :name "jimmy",:sanity-rating 9}
1 { :enjoy-clojure? true, :name "johnny",:sanity-rating 2}
2 { :enjoy-clojure? true, :name "jilly",:sanity-rating 5}
3 { :enjoy-clojure? true, :name "janey",:sanity-rating 8}
4 {:enjoy-clojure? false, :name "jelly",:sanity-rating 10}})
(defn update-db [db]
(zipmap (keys db)
(map (fn [student]
(cond-> student
(:enjoy-clojure? student)
(assoc :sanity-rating -2)))
(vals db))))
(update-db student-database) ;;=>
{0 {:enjoy-clojure? false, :name "jimmy", :sanity-rating 9},
1 {:enjoy-clojure? true, :name "johnny", :sanity-rating -2} ...}
the simplest would be like this:
(reduce-kv (fn [acc idx row]
(assoc acc idx
(if (:enjoy-clojure? row)
(assoc row :sanity-rating -2)
row)))
{}
student-database)
;;=> {0 {:enjoy-clojure? false, :name "jimmy", :sanity-rating 9},
;; 1 {:enjoy-clojure? true, :name "johnny", :sanity-rating -2},
;; 2 {:enjoy-clojure? true, :name "jilly", :sanity-rating -2},
;; 3 {:enjoy-clojure? true, :name "janey", :sanity-rating -2},
;; 4 {:enjoy-clojure? false, :name "jelly", :sanity-rating 10}}
you can also do something like this:
(reduce-kv (fn [res k {ec? :enjoy-clojure?}]
(if ec?
(assoc-in res [k :sanity-rating] -2)
res))
student-database
student-database)
;;=> {0 {:enjoy-clojure? false, :name "jimmy", :sanity-rating 9},
;; 1 {:enjoy-clojure? true, :name "johnny", :sanity-rating -2},
;; 2 {:enjoy-clojure? true, :name "jilly", :sanity-rating -2},
;; 3 {:enjoy-clojure? true, :name "janey", :sanity-rating -2},
;; 4 {:enjoy-clojure? false, :name "jelly", :sanity-rating 10}}

Removing nested values with Specter in Clojure

Suppose that I have a Clojure map like this:
(def mymap {:a [1 2 3] :b {:c [] :d [1 2 3]}})
I would like a function remove-empties that produces a new map in which entries from (:b mymap) that have an empty sequence as a value are removed. So (remove-empties mymap) would give the value:
{:a [1 2 3] :b {:d [1 2 3]}}
Is there a way to write a function to do this using Specter?
Here's how to do it with Specter:
(use 'com.rpl.specter)
(setval [:b MAP-VALS empty?] NONE my-map)
=> {:a [1 2 3], :b {:d [1 2 3]}}
In English, this says "Under :b, find all the map values that are empty?. Set them to NONE, i.e. remove them."
(update my-map :b (fn [b]
(apply dissoc b
(map key (filter (comp empty? val) b)))))
This is the specter solution:
(ns myns.core
(:require
[com.rpl.specter :as spc]))
(def my-map
{:a [1 2 3]
:b {:c []
:d [1 2 3]}})
(defn my-function
[path data]
(let [pred #(and (vector? %) (empty? %))]
(spc/setval [path spc/MAP-VALS pred] spc/NONE data)))
;; (my-function [:b] my-map) => {:a [1 2 3]
;; :b {:d [1 2 3]}}
I don't know specter either, but this is pretty simple to do in plain clojure.
(defn remove-empties [m]
(reduce-kv (fn [acc k v]
(cond (map? v) (let [new-v (remove-empties v)]
(if (seq new-v)
(assoc acc k new-v)
acc))
(empty? v) acc
:else (assoc acc k v)))
(empty m), m))
Caveat: For extremely nested data structures it might stack overflow.
So far I haven't found an approach with specter's filterer, because when I test filterers they seem to receive each map entry twice (once as a map entry and once as a 2-length vector) and giving different results between those seems to cause issues. However, we shouldn't be removing empty sequences anywhere they might appear, just map entries where they're the value.
I did seem to get a clojure.walk approach working that might still interest you, though.
(ns nested-remove
(:require [com.rpl.specter :as s]
[clojure.walk :refer [postwalk]]))
(defn empty-seq-entry? [entry]
(and (map-entry? entry) (sequential? (val entry)) (empty? (val entry))))
(defn remove-empties [root]
(postwalk #(if (map? %) (into (empty %) (remove empty-seq-entry? %)) %) root))
(remove-empties mymap) ;;=> {:a [1 2 3], :b {:d [1 2 3]}}
Assuming we only need to go one level deep and not search recursively like the accepted answer:
(setval [:b MAP-VALS empty?] NONE mymap)
A fully recursive solution that removes empty values in a map at any level
(def my-complex-map {:a [1] :b {:c [] :d [1 2 3] :e {:f "foo" :g []}}})
; declare recursive path that traverses map values
(declarepath DEEP-MAP-VALS)
(providepath DEEP-MAP-VALS (if-path map? [MAP-VALS DEEP-MAP-VALS] STAY))
(setval [DEEP-MAP-VALS empty?] NONE my-complex-map)
; => {:a [1], :b {:d [1 2 3, :e {:f "foo"}}}}
Reference the wiki on using specter recursively.
While I am not very familiar with Specter, in addition to the postwalk solution, you can solve this using tupelo.forest from the Tupelo library. You do need to rearrange the data a bit into Hiccup or Enlive format, then it's easy to identify any nodes with no child nodes:
(ns tst.clj.core
(:use clj.core tupelo.test)
(:require
[tupelo.core :as t]
[tupelo.forest :as tf] ))
(t/refer-tupelo)
(defn hid->enlive [hid]
(tf/hiccup->enlive (tf/hid->hiccup hid)))
(defn empty-kids?
[path]
(let [hid (last path)
result (and (tf/node-hid? hid)
(empty? (grab :kids (tf/hid->tree hid))))]
result))
; delete any nodes without children
(dotest
(tf/with-forest (tf/new-forest)
(let [e0 {:tag :root
:attrs {}
:content [{:tag :a
:attrs {}
:content [1 2 3]}
{:tag :b
:attrs {}
:content [{:tag :c
:attrs {}
:content []}
{:tag :d
:attrs {}
:content [1 2 3]}
]}]}
root-hid (tf/add-tree-enlive e0)
empty-paths (tf/find-paths-with root-hid [:** :*] empty-kids?)
empty-hids (mapv last empty-paths)]
(is= (hid->enlive root-hid) ; This is the original tree structure (Enlive format)
{:tag :root,
:attrs {},
:content
[{:tag :a,
:attrs {},
:content
[{:tag :tupelo.forest/raw, :attrs {}, :content [1]}
{:tag :tupelo.forest/raw, :attrs {}, :content [2]}
{:tag :tupelo.forest/raw, :attrs {}, :content [3]}]}
{:tag :b,
:attrs {},
:content
[{:tag :c, :attrs {}, :content []}
{:tag :d,
:attrs {},
:content
[{:tag :tupelo.forest/raw, :attrs {}, :content [1]}
{:tag :tupelo.forest/raw, :attrs {}, :content [2]}
{:tag :tupelo.forest/raw, :attrs {}, :content [3]}]}]}]})
(apply tf/remove-hid empty-hids) ; remove the nodes with no child nodes
(is= (hid->enlive root-hid) ; this is the result (Enlive format)
{:tag :root,
:attrs {},
:content
[{:tag :a,
:attrs {},
:content
[{:tag :tupelo.forest/raw, :attrs {}, :content [1]}
{:tag :tupelo.forest/raw, :attrs {}, :content [2]}
{:tag :tupelo.forest/raw, :attrs {}, :content [3]}]}
{:tag :b,
:attrs {},
:content
[{:tag :d,
:attrs {},
:content
[{:tag :tupelo.forest/raw, :attrs {}, :content [1]}
{:tag :tupelo.forest/raw, :attrs {}, :content [2]}
{:tag :tupelo.forest/raw, :attrs {}, :content [3]}]}]}]})
(is= (tf/hid->hiccup root-hid) ; same result in Hiccup format
[:root
[:a
[:tupelo.forest/raw 1]
[:tupelo.forest/raw 2]
[:tupelo.forest/raw 3]]
[:b
[:d
[:tupelo.forest/raw 1]
[:tupelo.forest/raw 2]
[:tupelo.forest/raw 3]]]])
)))

Merging two lists of maps where entries are identified by id in Clojure

What's the idiomatic way of merging two lists of maps in Clojure where each map entry is identified by an id key?
What's an implementation for foo so that
(foo '({:id 1 :bar true :value 1}
{:id 2 :bar false :value 2}
{:id 3 :value 3})
'({:id 5 :value 5}
{:id 2 :value 2}
{:id 3 :value 3}
{:id 1 :value 1}
{:id 4 :value 4})) => '({:id 1 :bar true :value 1}
{:id 2 :bar false :value 2}
{:id 3 :value 3}
{:id 4 :value 4}
{:id 5 :value 5})
is true?
(defn merge-by
"Merges elems in seqs by joining them on return value of key-fn k.
Example: (merge-by :id [{:id 0 :name \"George\"}{:id 1 :name \"Bernie\"}]
[{:id 2 :name \"Lara\"}{:id 0 :name \"Ben\"}])
=> [{:id 0 :name \"Ben\"}{:id 1 :name \"Bernie\"}{:id 2 :name \"Lara\"}]"
[k & seqs]
(->> seqs
(map (partial group-by k))
(apply merge-with (comp vector
(partial apply merge)
concat))
vals
(map first)))
How about this:
(defn foo [& colls]
(map (fn [[_ equivalent-maps]] (apply merge equivalent-maps))
(group-by :id (sort-by :id (apply concat colls)))))
This is generalized so that you can have an arbitrary number of input sequences, and an arbitrary grouping selector:
(def a [{:id 5 :value 5}
{:id 2 :value 2}
{:id 3 :value 3}
{:id 1 :value 1}
{:id 4 :value 4}])
(def b [{:id 1 :bar true :value 1}
{:id 2 :bar false :value 2}
{:id 3 :value 3}])
(def c [{:id 1 :bar true :value 1}
{:id 2 :bar false :value 2}
{:id 3 :value 3}
{:id 4 :value 4}
{:id 5 :value 5}])
(defn merge-vectors
[selector & sequences]
(let [unpack-grouped (fn [group]
(into {} (map (fn [[k [v & _]]] [k v]) group)))
grouped (map (comp unpack-grouped (partial group-by selector))
sequences)
merged (apply merge-with merge grouped)]
(sort-by selector (vals merged))))
(defn tst
[]
(= c
(merge-vectors :id a b)))