UpdateWhere is a handy function
twitter.comFeels like something had gone very wrong if a function like this is useful.
Would only be useful for unstructured data since if you had structure data you could write a more specific function.
This can easily match the wrong value and cause problems. And why would it match the wrong value? Because you have unstructured data and it might not be knowable if you might have false positives
This is often useful if you're rewriting something that looks like an abstract syntax tree.
> And why would it match the wrong value?
Because you may be searching the tree for something that looks like `(op(const)(const))` to replace it with `(const)`. But that may live many levels deep inside the structure.
Also, from existing names, that seems very close to MapIf (https://resources.wolframcloud.com/FunctionRepository/resour...) just with added recursion.
I don't like how it could result in surprises. e.g. you use it to update an array, but there are objects inside, which you accidentally modify.
I think these surprises could be reduced if you turn them into explicit parameters in the signature, e.g.
export const updateWhere = (whereFn, updatefn, thing, recurseArray=true , recurseObjects=true)If `thing` contains any cycles then it will explode. E.g.
orconst thing = {}; thing.self = thing;
If this sounds far-fetched to you just know that all DOM elements have these circular references through (for example) parent / child references.const thing = []; thing.push(thing);Also any non-enumerable properties that may have been defined on objects are lost.
Basically the function is only suitable for recently parsed JSON.
That's a great point
I disagree. For example, It's useful if you have a bunch of cached network requests that each list some resources and you want to update all of the instances of a specific resource (matching by uuid).
So store the requests in a map and each request exposes an update() method.
I don’t know the domain where this function is used, but stuff like that is used constantly in data science.
df[df[mySelection] == wrongVal] = rightVal
Function-ified visitor pattern I guess? Python's NodeTransformer[0] is a good example of a thing like this though only across AST nodes.
Going along some previous discussions about jq, and stuff like specter (for Clojure), I do think it would be cool if there were more syntactic niceties for path expressions, extracting data from nested data structures/lists/etc, and applying transformations on them.
Specter can have a "nice DSL" (for certain definitions of nice!) thanks to Clojure's macro functionality. Haskell lenses get somewhere thanks to custom infix operator syntax being a thing. I have yet to see a library that tackles this nicely without those two tools though.
[0]: https://docs.python.org/3/library/ast.html#ast.NodeTransform...
This is a simple function that I find quite useful. Crawl through an object to update some specific nested value. Useful for things like optimistic updates in a cache (eg. Find every instance of an object with some guid in any random data structure and update a field in it).
Is this function known by any other name?
Disliking the generality of this function (as a sibling commenter said.. I mean with the wrong predicate you won't even get to test for list or dict.. but ok for those nested list/dict structures; but why not test first for them?) it is just a map and an if, or a map/list comprehension?
I mean, the paradigm would be a iterable-based dispatch. There's no name (that I know of) for such a specific (and yet, weirdly general) implementation.
I agree with many others in this thread when it comes to this being too specific to be useful and too generic to keep you from shooting yourself in the foot. I would, instead, add this to Array/Object's prototypes and call it on the specific types instead. At least, in that case, you aren't handling multiple fields of concern.
Maybe related to "Scrap your boilerplate"[1] which seems the more general approach.
[1] https://www.microsoft.com/en-us/research/wp-content/uploads/...
basically fmap in haskell. A more complete implementation would have the iterating logic distributed and owned by the prototypes of any arbitrary data container, dynamic dispatch ftw.
Clever is bad, boring is good.
As a senior engineer, you see things like this all the time from more junior engineers: way too generic code, done in an overly clever way, incompressible to most. I've spent countless hours telling junior engineers to "dumb it down" and "make it less generic". Not all features that exist must be used. And no, you don't have to combine them all either. That's why I dislike Scala, and like Go. Go has like 3 features, and Scala has 100. It's very hard to make something overly complicated with 3 features (this is hyperbole obviously;I like Scala as a language, but not how many humans use it).
Code is written once, and read 100x, so it must be easy to parse by humans, and easy to understand in a split second. Junior engineers will have to understand it. New team members will have to understand it. If you have the occasional function like this, that's fine. If your entire code looks like this, or you are proud to have written a "clever function", that's not so good.
I try to write dumb code wherever possible. Do the expected, do the boring.
Is this really so hard to read?
The alternatives as far as I can tell are:updateWhere( thing => thing?.guid === postId, post => ({...post, title: newTitle}), networkCache )- normalize network responses, so you can get the post directly by id. But this is notoriously painful to do well
- or the code that handles this name change needs to know every request that references the post with that id, and update those titles manually
This reminds me of Haskell's 'functor' [1]
[1] https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-...
Very much so, it’s a functor instance on some sort of tree, except the `map` includes a conditional dispatch.
It’s essentially
Except this doesn’t quite work, I’m sure there’s a pointfree version, best I can achieve isfmap $ ap fromMaybe
Which takes an `a -> Maybe a` and an `[a]`, and replaces all the values for which the callback returns `Some`, and leaves as-is all the values for which it returns `None`.\p -> fmap $ ap fromMaybe p(I collapsed the `whereFn` and `updateFn` into a single one because there’s no reason not to)
You're looking for `fmap . ap fromMaybe`
This seems like ramda’s evolve function because it updates with a function instead of a value, and it’s conditional because you’re giving a predicate.
One idea to make this more versatile would be to use a mapping instead of a callable for the whereFn and updateFn in order to simultaneously alter multiple columns of your objects. Also, you could add a path field in order to focus on particular nested paths in your data.
Ultimately, others might be right; this function is intended for use on collections, make sure to avoid using this to enable yourself to have a messy junk drawer of json blobs!
This feels recursion-schemesey. Something like:
para (\t -> let asIs = (map fst t) in if(whereFn(asIs)) updateFn(asIs) else map snd t)Also, can one maybe view updateWith as taking whereFn and returning a kind of lens?
No, I don't think so. How would that work?
Please no
Something is very wrong if you need these.
Get help. Use a hashmap. Plz
this is a toxic comment, and rather presumptive. You don't know what this person's use-case is or what constraints they have.
I've worked with enough toxic codebases like this. It seems to rub off :)
This function can get stuck in an infinite loop.
This function isn't generic, doesn't return an IEnumerable, raises the question why it only works for arrays...
Not great lol
Just use JSON.stringify and then JSON.parse with a custom replacer method if you know it's a simple JSON object.