001  (ns sparse-array.extract
002    "Extracting interesting data from sparse arrays."
003    (:require [sparse-array.core :refer [*safe-sparse-operations*
004                                         dense-array? dense-dimensions
005                                         make-sparse-array sparse-array?]]))
006  
007  ;;; The whole point of working with sparse arrays is to work with interesting
008  ;;; subsets of arrays most of which are uninteresting. To extract an
009  ;;; interesting subset from an array, we're going to need an extract function.
010  
011  (defn- extract-from-sparse
012    "Return a subset of this sparse `array` comprising all those cells for which
013    this `function` returns a 'truthy' value."
014    [array function]
015    (reduce
016      merge
017      (apply
018        make-sparse-array
019        (cons
020          (:coord array)
021          (when (coll? (:content array)) (:content array))))
022      (map
023        #(if
024           (= :data (:content array))
025           (when (function (array %)) {% (array %)})
026           (let [v (extract-from-sparse (array %) function)]
027             (if
028               (empty?
029                 (filter integer? (keys v)))
030               nil
031               {% v})))
032        (filter integer? (keys array)))))
033  
034  (defn extract-from-dense
035    "Return a subset of this dense `array` comprising all those cells for which
036    this `function` returns a 'truthy' value. Use these `axes` if provided."
037    ([array function]
038     (extract-from-dense array function (map #(keyword (str "i" %)) (range))))
039    ([array function axes]
040     (let
041       [dimensions (dense-dimensions array)]
042       (reduce
043         merge
044         (apply make-sparse-array (take dimensions axes))
045         (if
046           (= dimensions 1)
047           (map
048             (fn [i v] (when (function v) (hash-map i v)))
049             (range)
050             array)
051           (map
052             (fn [i v] (if (empty? (filter integer? (keys v))) nil (hash-map i v)))
053             (range)
054             (map #(extract-from-dense % function (rest axes)) array)))))))
055  
056  (defn extract
057    "Return a sparse subset of this `array` - which may be either sparse or
058    dense - comprising all those cells for which this `function` returns a
059    'truthy' value."
060    [array function]
061    (cond
062      (sparse-array? array)
063      (extract-from-sparse array function)
064      (dense-array? array)
065      (extract-from-dense array function)
066      *safe-sparse-operations*
067      (throw
068        (ex-info
069          "Argument passed as `array` is neither sparse nor dense."
070          {:array array}))))
071