当值包含复合数据时,在Clojure中按值筛选hashmap
我在努力自学Clojure 对于一个与工作相关的项目(很明显,我不是一个专业的程序员),我尝试将一系列电子表格结合起来。电子表格中有与金融交易相关的注释。多个注释(包括电子表格中的注释)可以引用同一交易;每个事务都有一个唯一的序列号。因此,我使用以下数据结构来表示电子表格:当值包含复合数据时,在Clojure中按值筛选hashmap,clojure,hashmap,Clojure,Hashmap,我在努力自学Clojure 对于一个与工作相关的项目(很明显,我不是一个专业的程序员),我尝试将一系列电子表格结合起来。电子表格中有与金融交易相关的注释。多个注释(包括电子表格中的注释)可以引用同一交易;每个事务都有一个唯一的序列号。因此,我使用以下数据结构来表示电子表格: (def ss { :123 '([ "comment 1" "comment 2" ] [ "comment 3" "comment 4" ] [ "co
(def ss { :123 '([ "comment 1" "comment 2" ]
[ "comment 3" "comment 4" ]
[ "comment 5" ]),
:456 '([ "happy days" "are here" ]
[ "again" ])})
这可以通过以下两个电子表格创建:
+------------+------------+-----------+
| Trans. No. | Cmt. A | Cmt. B |
+------------+------------+-----------+
| 123 | comment 1 | comment 2 |
| 456 | happy days | are here |
| 123 | comment 3 | comment 4 |
+------------+------------+-----------+
+-----------------+------------+
| Analyst Comment | Trans. No. |
+-----------------+------------+
| comment 5 | 123 |
| again | 456 |
+-----------------+------------+
我已经成功地编写了函数来创建这个数据结构,给出了一个充满CSV的目录。我想再写两个函数:
;; FUNCTION 1 ==========================================================
;; Regex Spreadsheet -> Spreadsheet ; "Spreadsheet" is like ss above
;; Produces a Spreadsheet with ALL comments per transaction if ANY
;; value matches the regex
; (defn filter-all [regex my-ss] {}) ; stub
(defn filter-all [regex my-ss] ; template
(... my-ss))
(deftest filter-all-tests
(is (= (filter-all #"1" ss)
{ :123 '([ "comment 1" "comment 2" ]
[ "comment 3" "comment 4" ]
[ "comment 5" ]) })))
;; FUNCTION 2 ==========================================================
;; Regex Spreadsheet -> Spreadsheet ; "Spreadsheet" is like ss above
;; Produces a Spreadsheet with each transaction number that has at least
;; one comment that matches the regex, but ONLY those comments that
;; match the regex
; (defn filter-matches [regex my-ss] {}) ; stub
(defn filter-matches [regex my-ss] ; template
(... my-ss))
(deftest filter-matches-tests
(is (= (filter-matches #"1" ss)
{ :123 '([ "comment 1" ]) })))
我不明白的是,对于每个键
,如果它们是嵌套在列表中的向量中的字符串,那么最好的方法是将正则表达式足够深入地放入VAL
。我曾尝试将filter
与嵌套的apply
s或map
s一起使用,但我对语法感到困惑,即使它起作用,我也不知道如何使用键来构建新的hashmap
我也尝试过在filter
函数中使用解构,但我也感到困惑,我还认为我必须跨嵌套数据“提升”函数(我认为这是Haskell中的术语,如应用程序和monad)
有人能建议过滤此数据结构的最佳方法吗?作为另一件事,我很乐意就这是否是一个适合我的合理数据结构获得反馈,但我希望了解如何解决目前存在的问题,如果只是为了学习的目的
非常感谢。这里有一个与您的数据结构相关的解决方案。
filter
采用谓词函数。在这个函数中,您实际上可以在数据结构中测试您需要的任何东西。在这里,flatten
有助于删除注释向量列表
(defn filter-all [regex my-ss]
(into {} (filter (fn [[k v]] ; map entry can be destructured into a vector
; flatten the vectors into one sequence
; some return true if there is a match on the comments
(some #(re-matches regex %) (flatten v)))
my-ss)))
user> (filter-all #".*3.*" ss)
{:123 (["comment 1" "comment 2"] ["comment 3" "comment 4"] ["comment 5"])}
对于过滤器匹配
来说,逻辑是不同的:您希望使用值的某些部分构建一个新映射<代码>减少
有助于做到这一点:
(defn filter-matches [regex my-ss]
(reduce (fn [m [k v]] ; m is the result map (accumulator)
(let [matches (filter #(re-matches regex %) (flatten v))]
(when (seq matches)
(assoc m k (vec matches)))))
{}
my-ss))
user> (filter-matches #".*days.*" ss)
{:456 ["happy days"]}
对于数据结构本身,如果没有必要将嵌套向量保留在每个条目的列表中,您可以使用
{:123[“comment1”“comments 2”]…}
进行简化,但这不会大大简化上述函数。我认为您的方法是正确的,但可能会使生活变得比需要的困难一些
最大的问题是正则表达式的使用。虽然regexp在某些方面是一个很好的工具,但在其他解决方案更好、速度更快的时候,经常使用它
clojure采用的一个关键思想是使用小型库,将它们组装在一起以获得更高级别的抽象。例如,有各种库用于处理不同的电子表格格式,如excel、google docs电子表格,并且支持处理CSV文件。因此,我的第一步是看看您是否能找到一个库,将您的spreadhseet解析为标准的clojure数据结构
例如,clojure的data.csv将csv电子表格处理成一个向量的惰性序列,其中每个向量是电子表格中的一行,向量中的每个元素是该行中的一列值。一旦你有了这种格式的数据,然后用map、filter等处理它就相当简单了
下一步是考虑将使处理尽可能简单的抽象类型。这在很大程度上取决于您计划做什么,但我对这类数据的建议是使用一个嵌套结构,该结构由哈希映射组成,在外层由您的事务号索引,然后每个值都是一个哈希映射,该哈希映射在电子表格中的每一列都有一个条目
{:123 {:cmnta ["comment 1" "comment 3"]
:cmntb ["comment 2" "comment 4"]
:analstcmt ["comment 5"]}
:456 {:cmnta ["happy days"]
:cmntb ["are here"]
:analystcmt ["again"]}}
有了这个结构,您就可以使用诸如get in和update in之类的函数来访问/更改结构中的值,即
(get-in m [123 :cmnta]) => ["comment 1" "comment 3"]
(get-in m [123 :cmnta 0]) => "comment 1"
(get-in m [456 :cmnta 1]) => nil
(get-in m [456 :cmnta 1] "nothing to see here - move on") => "nothing to see here - move on"
我假设不同的评论向量来自不同的CSV文件?是否要求将它们分开?如果不存储它们来自何处的信息,这似乎是一个不必要的复杂问题。它们是同一行上的不同细胞。我可能可以去掉它们,但我也希望有一个选项,可以用匹配的单元格显示整行,或者只显示匹配的单元格。谢谢,这是一个有用的补充。我使用data.csv获得了我上面描述的数据结构,但可能我对它的切割程度超出了需要,并且削弱了使用抽象的能力。也就是说,就我而言,正则表达式可能是有用的(我是一名律师,我们正在使用各种搜索词来查看交易的评论是否值得一个人查看)。正则表达式可能有用,但要小心。锚定正则表达式以确保性能不会受到很大影响,这一点非常重要。尽管您可能不想走得更远,但通过查看clojure中正在进行的一些自然语言处理(NLP)工作,您可能会做得更好。