weavejester / medley

A lightweight library of useful Clojure functions
Eclipse Public License 1.0
870 stars 67 forks source link

Add assoc-in-some function #80

Open erikcc02 opened 1 year ago

erikcc02 commented 1 year ago

Description:

This Pull Request adds the assoc-in-some function to the repository, complementing the existing assoc-some function. The assoc-in-some function was implemented to make it possible to associate values in nested associative structures, as long as the value is not null (nil).

Motivation:

The assoc-some function currently supports conditional binding on a single map level, allowing you to bind a value to a key only if that value is not null. However, this functionality is limited to top-level map structures and does not cover associations in nested structures such as maps within maps.

The new assoc-in-some function aims to fill this gap by allowing conditional associations at any level of a nested associative structure, such as maps within maps, vectors within maps, etc.

By providing a convenient way to perform conditional associations on nested structures, the assoc-in-some function simplifies code. It improves the readability of programs that deal with complex, nested data.

Example of use:

(def data {:user {:name "John" :email "john@example.com"}
           :preferences {:theme "dark" :notifications true}})

;; If the email is not null, it will be associated with the user
(def updated-data (assoc-in-some data [:user :email] "newemail@example.com"))

;; If the theme value is not null, it will be associated with the theme preference
(def updated-data (assoc-in-some data [:preferences :theme] "light"))

;; If the value passed is null, the original structure will be kept
(def updated-data (assoc-in-some data [:user :name] nil))
weavejester commented 1 year ago

I'm on the fence about this. Could you give some reasons why using update and update-in are not sufficient?

(update data :user assoc-some :name nil)
(assoc-in-some data [:user :name] nil)

(update-in data [:account :user] assoc-some :name nil)
(assoc-in-some data [:account :user :name] nil)

Also, what are your thoughts regarding assoc-in-some vs. assoc-some-in?

Finally, can you change the commit message to Add assoc-in-some function for consistency with the commit log.

weavejester commented 1 year ago

Actually, see: https://github.com/weavejester/medley/pull/72#issuecomment-1447318586. I'm thinking the same reasoning applies here:

(some->> v (assoc-in data [:account :name]))
(assoc-in-some data [:account :user :name] v)

Given that this function would require more characters than the existing idiomatic solution, I'm inclined to close this for the same reasons as last time.

NoahTheDuke commented 1 year ago

The some->> example is missing one of the keys which is why it's shorter lol.

erikcc02 commented 1 year ago

@weavejester So, like https://github.com/weavejester/medley/pull/72#issuecomment-1448217787, I was also under the impression that separating this into a new helper function makes writing clearer.

When a person uses the assoc-some function, soon comes the need to have an assoc-in-some, maybe that's why other people also made this suggestion.

But I don't see any problems using update either :)

erikcc02 commented 1 year ago

I updated the commit message

erikcc02 commented 1 year ago

Also, what are your thoughts regarding assoc-in-some vs. assoc-some-in?

Looking at the Clojure documentation, some functions end with -in, but I like assoc-in-some because, in my opinion, it is more in line with the assoc-in function, just adding -some to the end

weavejester commented 1 year ago

The some->> example is missing one of the keys which is why it's shorter lol.

Whoops!!

(some->> v (assoc-in data [:account :user :name]))
(assoc-in-some data [:account :user :name] v)

So I guess it's 5 characters shorter.

erikcc02 commented 1 year ago

@weavejester I was seeing that this function exists in another clojure lib: clojure-lsp https://cljdoc.org/d/com.github.clojure-lsp/clojure-lsp/2023.04.19-12.43.29/api/clojure-lsp.shared#assoc-in-some

I think having assoc-some and assoc-in-some in this library standardizes this kind of action well rather than using assoc-some and update or defining this function in your local project as a helper