How to Cheat in JS Object Manipulation
Tags: programming, javascript, webdev
Published: 2023-06-08 12:07:30

Often in JavaScript, we’ve got to manipulate JavaScript `Object`s in order to serve a purpose. Today I’m going to cover a quick way to cheat in this that I don’t see nearly as often, and it has a couple of uses. This cheat implicates `JSON.parse()` and `JSON.stringify()`.
 
Many folks who have already been using JavaScript for a while will already be familiar with our friends, the `JSON.*` functions, but they’ve got sneaky extra features (that really aren’t that sneaky and make complete sense). I’ve beaten around the bush too much already, let’s get down to it.

## The `Object`

The rest of this article I’m going to assume that “the object” refers to this snippet right here:

```js
// It's an Object keyed by SKU,
// with product definitions as values.
{
  'RDCCAY': {
    name: 'red crayon',
    brand: 'Crayola',
    color: 'red',
    onhand: 524
  },
  'RDCOHA': {
    name: 'red crayon',
    brand: 'Ohio Art',
    color: 'red',
    onhand: 264
  },
  'GNCCAY': {
    name: 'green crayon',
    brand: 'Crayola',
    color: 'green',
    onhand: 453
  },
  '5PKGCO': {
    name: 'green crayon, 5-pack',
    brand: 'Ohio Art',
    color: 'green',
    onhand: 15
  },
}
```

You can see that this is a list of products, organized by SKU, and each item has a `name`, `brand`, `color`, and `onhand` field.

## The Problem

You're working your normal day, doing webdev stuff (webdev stuff is your favorite stuff), when your phone rings! No, it's not a time machine, you've been ordered back to the office and someone thought you needed a desk phone! You have mixed feelings about this, but you relent to the buzzing of the phone and answer it. It's someone from the warehouse!

You like the warehouse people, but they're calling you with bad news: the warehouse had an air conditioner fail and all of the Ohio Art crayons were in that zone and have melted! The horror: soft, oozy crayon wax everywhere, it'll take months for them to clean it up, and you express your sympathy. As a result, the warehouse director is asking you to go into set the on-hand inventory to zero so that we don't take orders for the no-longer-sellable product.

Also, this is rather urgent, so do hurry.

## The Technical Decision

When you're a tech worker, every function is a technical choice, though admittedly not always a large or consequential one. This time is no different, so let's find the intersection of business and technical here.

- Every second, your users might be purchasing a product that your warehouse is now telling you they can't ship.
  - Customers will be upset
  - Customer Service will be inundated with calls and returns
- This is a quick-and-dirty scenario, so elegance and performance aren't your priority; speed is.

## The Quick-&-Dirty

```js
const updatesToApply = (inputObj) => JSON.parse(
  JSON.stringify(inputObj),
  (key,val) => val.brand === 'Ohio Art' ?
    Object.assign(val, {onhand:0}) : val
)
```

Here's your logic:

1. First, stringify your object with `JSON.stringify()`
2. Then parse it again using `JSON.parse()`
3. In your `reviver` function, if the brand is "Ohio Art" then set the on-hand to zero

The `JSON.parse()` function will replace the value in the re-hydrated object with the return value of the `reviver` function. This allows you to easily manipulate any object where the name and the value of an `Object` is all you need in order to determine what change to the value should be.

And it works!

```sh
$ node ./demo1.js 
{
  RDCCAY: {
    name: 'red crayon',
    brand: 'Crayola',
    color: 'red',
    onhand: 524
  },
  RDCOHA: {
    name: 'red crayon',
    brand: 'Ohio Art',
    color: 'red',
    onhand: 0
  },
  GNCCAY: {
    name: 'green crayon',
    brand: 'Crayola',
    color: 'green',
    onhand: 453
  },
  '5PKGCO': {
    name: 'green crayon, 5-pack',
    brand: 'Ohio Art',
    color: 'green',
    onhand: 0
  }
}
```

Some could correctly point out that this function uses a lot more memory and processing power than a more elegant recursive function, or iterative function, and that it will run much slower than if you had done this with code. To those, I would point out the following:

1. Recursion can be pretty memory intensive, too
2. `JSON.stringify()` and `JSON.parse()` are built-in functions, and you'd be surprised how well they perform compared to user-defined functions
3. YOU ARE IN A HURRY! THINK OF THE CUSTOMERS!

## One More Thing...

While this `reviver` function is helpful for changing the value, it can also remove the element from the re-hydrated object entirely.

This is the same snippet, but instead of setting on-hand to zero it simply removes the SKU from the `Object`:

```js
const updatesToApply = (inputObj) => JSON.parse(
  JSON.stringify(inputObj),
  (key,val) => val.brand === 'Ohio Art' ?
    undefined : val
)
```

If your `reviver` function returns `undefined` then `JSON.parse()` will exclude that node from its output entirely.

It'll look a little like this:

```sh
$ node ./demo1.js 
{
  RDCCAY: {
    name: 'red crayon',
    brand: 'Crayola',
    color: 'red',
    onhand: 524
  },
  GNCCAY: {
    name: 'green crayon',
    brand: 'Crayola',
    color: 'green',
    onhand: 453
  }
}
```

## Conclusion

It's important that as we make our technical choices, that we all remember that at work we are also business people. If our code isn't serving our business and our customers, we aren't doing our jobs very well.

The most elegant and performant code in the world will not stop a customer from being upset if their order doesn't go smoothly.

Have a great day!