(def bulk-map (arr fn)
  (var index 0
          group-size fn.length
          ret-arr [])

  (while (< index arr.length)
    (.push ret-arr
          (apply fn (send arr slice
                          index (+ index group-size))))
    (incr-by index group-size))
  ret-arr)

(def inject (start items fn)
     (if (list? items)
         (items.reduce fn start)
         start))

(def map (items fn)
     (if (list? items)
         (items.map fn)
         []))

(def select (items fn)
     (if (list? items)
         (items.filter fn)
         []))

(def detect (items fn)
     (when (list? items)
         (items.find fn)))

(def all? (items fn)
     (when (list? items)
           (items.every fn)))

(def none? (items fn)
     (when (list? items)
           (not (items.some fn))))

(def any? (items fn)
     (when (list? items)
           (items.some fn)))

(def reject (items fn)
  (select items (#> (not (apply fn arguments)))))


(def compact (arr)
     (select arr (#(item)
                   (and
                    (!= null item)
                    (!= false item)
                    (defined? item)))))

(def unique (arr)
     (inject [] arr
             (#(coll item)
               (if (includes? coll item)
                   coll
                   (coll.concat [item])))))


(def interleave (glue arr)
     (when (and (string? arr) (list? glue))
           (var temp glue)
           (assign glue arr
                   arr temp))
     
     (if (list? glue) (inject [] arr
             (#(collector item index)
               (collector.concat [item (get glue index)])))

         
     (inject [(first arr)] (rest arr)
             (#(collector item index) (collector.concat [glue item])))))


(def flatten (items predicate)
     (if (list? items)
         (inject [] items
                 (#(collector item)
                   (if (or (not predicate) (predicate item))
                       [ ...collector
                         ...(if (list? item) (flatten item predicate) item) ]
                       collector)))

         (or (not predicate) (predicate items)) [items]

         []))

(def flat-compact (items)
     (flatten items (#(item)
                      (and
                       (!= null item)
                       (!= false item)
                       (defined? item)))))

(def recurse-map (item fn)
     (if (list? item) (map item (#(subitem) (recurse-map subitem fn)))
         (fn item)))

(def pluck (items attribute)
     (map items (#(item) (get item attribute))))

(def merge-into (into from)
     (Object.assign into from))

(def clone (object)
     (Object.assign {} object))

(def values (object)
     (|> object keys (map (#> (get object #0)))))

(def map-values (object fn)
     (inject {} (keys object)
             (#(collector key index)
               (set collector key (fn (get object key) key))
               collector)))

(def merge-with (into from)
     (Object.assign {} into from))
