vectorclojureupdatesnull-pointernested-map

Updating values of map using for in Clojure


I have a json like below and have a requirement to add values in it

{ "10 days refund": [
        {
            "paymentType": "CreditCard",
            "amount": "40$",
            "expiryDate": "20/10/2025"
        },
        {
            "paymentType": "CreditCard",
            "amount": "20$",
            "expiryDate": "20/1/2024"
        }
    ],
    "3 hours refund": [
        {
            "paymentType": "DebitCard",
            "amount": "10$",
            "expiryDate": "20/10/2026"
        },
        {
            "paymentType": "DebitCard",
            "amount": "5$",
            "expiryDate": "20/10/2027"
        }
    ]
}

In the above map, I would need to add a field "message" to last value of each category of map. So after adding the value I'm expecting the resultant map like below

{ "10 days refund": [
            {
                "paymentType": "CreditCard",
                "amount": "40$",
                "expiryDate": "20/10/2025"
            },
            {
                "paymentType": "CreditCard",
                "amount": "20$",
                "expiryDate": "20/1/2024",
                "message" : "Refund will be processed in 10 days"
            }
        ],
        "3 hours refund": [
            {
                "paymentType": "DebitCard",
                "amount": "10$",
                "expiryDate": "20/10/2026"
            },
            {
                "paymentType": "DebitCard",
                "amount": "5$",
                "expiryDate": "20/10/2027",
                "message" : "Refund will be processed in 3 hours"
            }
        ]
    }

I have tried implementing it using below method

(defn ^:private payment-methods
  [pm-sorted]
  (let [categories (group-by pm-fingerprint pm-sorted)]
    (for [[pm-fingerprint pm-sorted] categories
          :let [last-payment-method (last pm-sorted)
                dropped-last (drop-last pm-sorted)]]
      (if (= (:refund-category pm-fingerprint) "10 hours refund")
        ((println "10 days refund")
         (doall (conj (doall dropped-last) (assoc last-payment-method :message
                                                                      (text (str "Refund will be processed in 10 days"))))))
        ((println "3 hours refund")
         (doall (conj (doall dropped-last) (assoc last-payment-method :message
                                                                      (text (str "Refund will be processed in 3 hours"))))))))))

(defn ^:private pm-fingerprint
  [payment-method]
  (let [payment-type ("paymentType" payment-method)]
    {:refund-category (if (= payment-type "CreditCard"))
                     "10 days refund"
                     "3 hours refund")}))

I expect the resultant output by executing the function to be a vector like below

[
{
                    "paymentType": "CreditCard",
                    "amount": "40$",
                    "expiryDate": "20/10/2025"
                },
                {
                    "paymentType": "CreditCard",
                    "amount": "20$",
                    "expiryDate": "20/1/2024",
                    "message" : "Refund will be processed in 10 days"
                }
                {
                    "paymentType": "DebitCard",
                    "amount": "10$",
                    "expiryDate": "20/10/2026"
                },
                {
                    "paymentType": "DebitCard",
                    "amount": "5$",
                    "expiryDate": "20/10/2027",
                    "message" : "Refund will be processed in 3 hours"
                }
            ]

And I get null as the output as no value is returned. Can someone please help wrt the issue ?


Solution

  • Here is an example that works. First, declarations & data:

    (ns tst.demo.core
      (:use tupelo.core tupelo.test)
      (:require
        [schema.core :as s]
        [tupelo.string :as str]
      ))
    
    (def input
      (str/quotes->double
        "{ '10-days-refund': [
                { 'paymentType': 'CreditCard',
                    'amount': '40$',
                    'expiryDate': '20/10/2025' },
                { 'paymentType': 'CreditCard',
                    'amount': '20$',
                    'expiryDate': '20/1/2024' }
            ],
            '3-hours-refund': [
                {
                    'paymentType': 'DebitCard',
                    'amount': '10$',
                    'expiryDate': '20/10/2026' },
                { 'paymentType': 'DebitCard',
                    'amount': '5$',
                    'expiryDate': '20/10/2027' } ] }"))
    

    Then the processing:

    (dotest
      (let [data  (json->edn input)
            data2 (vec
                    (for [entry data]
                      (let [[the-key pmt-lst] entry ; destruture into key and list of vals
                            pmt-lst-butlast   (butlast pmt-lst)
                            pmt-lst-last      (last pmt-lst)
                            last-new          (assoc pmt-lst-last :message "Coming Soon!")
                            pmt-lst-new       (append pmt-lst-butlast last-new)
                            entry-new         [the-key pmt-lst-new]]
                        entry-new)))]
    

    and the result verified:

        (is= data2
             [[:10-days-refund
               [{:amount "40$" :expiryDate "20/10/2025" :paymentType "CreditCard"}
                {:amount      "20$"
                 :expiryDate  "20/1/2024"
                 :message     "Coming Soon!"
                 :paymentType "CreditCard"}]]
              [:3-hours-refund
               [{:amount "10$" :expiryDate "20/10/2026" :paymentType "DebitCard"}
                {:amount      "5$"
                 :expiryDate  "20/10/2027"
                 :message     "Coming Soon!"
                 :paymentType "DebitCard"}]]]
        )))
    

    Please see the list of documentation in this template project, especially the Clojure CheatSheet.