如何用Specter实现按idx位置在有序列表中插入实体?
用Specter改写按idx有序插入元素的函数
问题背景
现有已按:idx字段有序排列的Clojure列表:
[{:identifier "Green", :idx 2} {:identifier "Indigo", :idx 4} {:identifier "Violet", :idx 5}]
需要插入如下实体:
{:identifier "Blue", :idx 3}
最终期望得到的结果为:
[{:identifier "Green", :idx 2} {:identifier "Blue", :idx 3} {:identifier "Indigo", :idx 4} {:identifier "Violet", :idx 5}]
失败的尝试
我尝试用以下Specter代码实现,但未达到预期效果:
(s/setval [s/ALL (s/pred #(> (:idx %) idx)) s/FIRST s/BEFORE-ELEM] {:identifier "Blue", :idx 3} xs)
需求目标
- 定位列表中第一个
:idx值大于传入idx参数的元素 - 在该元素的前面插入新实体
原Clojure实现
已用原生Clojure写出功能正常的函数:
(defn insert-inbetween-by-idx [xs identifier idx] (let [insert {:identifier identifier :idx idx} b4-idx (filterv (comp (partial > idx) :idx) xs) after-idx (filter (comp (partial < idx) :idx) xs)] (reduce into [b4-idx [insert] after-idx])))
Specter改写方案
利用Specter的导航器可以更简洁地实现该功能,改写后的函数如下:
(require '[com.rpl.specter :as s]) (defn insert-inbetween-by-idx [xs identifier idx] (let [insert {:identifier identifier :idx idx}] (s/setval [(s/first (s/pred #(> (:idx %) idx))) s/BEFORE-ELEM] insert xs)))
关键说明
(s/first (s/pred #(> (:idx %) idx))):精准定位列表中第一个:idx大于目标idx的元素s/BEFORE-ELEM:导航到该元素的前置位置,完成插入操作- 边界情况处理:如果列表中所有元素的
:idx都小于等于目标idx,新元素会自动插入到列表末尾
内容的提问来源于stack exchange,提问作者Chris Murphy
相关产品推荐
相关产品推荐

