按公共子字符串对字符串列表进行分组

Group list of strings by common substring

我有一个字符串列表,例如:

("2019_FOO_BAR.1_12"
 "2019_FOO_BAR.1_13"
 "2018_FOO_BAR.1_12"
 "2019_FOO_XYZ.1_14"
 "2017_FOO_BAR.1_14"
 "2017_FOO_XYZ.1_12"
 "2019_FOO_XYZ.1_13")

我想按第一个下划线之后和点之前的公共子字符串对它们进行分组。

在这个例子中,我有 2 个唯一的子字符串 FOO_BAR 和 FOO_XYZ。但是更长的列表可能有 N 个唯一的子字符串。

我希望结果如下所示:

(["2019_FOO_BAR.1_12" "2019_FOO_BAR.1_13" "2018_FOO_BAR.1_12" "2017_FOO_BAR.1_14"]
 ["2017_FOO_XYZ.1_12" "2019_FOO_XYZ.1_13" "2019_FOO_XYZ.1_14"])

因此每个子字符串都分组在一个单独的列表中

我想你正在寻找 group-by

(def test-data '("2019_FOO_BAR.1_12"
                 "2019_FOO_BAR.1_13"
                 "2018_FOO_BAR.1_12"
                 "2019_FOO_XYZ.1_14"
                 "2017_FOO_BAR.1_14"
                 "2017_FOO_XYZ.1_12"
                 "2019_FOO_XYZ.1_13"))


(defn string-to-key [^String input-string]
  (let [first-spliter (.indexOf input-string "_" )
        second-spliter (.indexOf input-string "." )]
    (.subSequence input-string (+ 1 first-spliter) second-spliter)))

因此,您可以通过以下方式准确获得所需内容:

(vals (group-by string-to-key test-data))

正则表达式的良好候选者:

user> (vals (group-by (partial re-find #"_.*?\.") data))

;; => (["2019_FOO_BAR.1_12"
;;      "2019_FOO_BAR.1_13"
;;      "2018_FOO_BAR.1_12"
;;      "2017_FOO_BAR.1_14"]
;;     ["2019_FOO_XYZ.1_14" "2017_FOO_XYZ.1_12" "2019_FOO_XYZ.1_13"])