JavaScript monads part 6

Mbedingfield
6 min readDec 4, 2022

--

items

Hopefully you have been following this post that starts here.

I know, so far, we have written everything from scratch and that’s what I like to see when I am looking at coding tutorials, but now I have to be that guy and ask that you copy some fake data. It’s ok, you don’t need to write all this code out, but here it is:

export const data = [
{
upc: '4011',
description: 'bananas',
dept: 5,
deptDescription: 'Produce',
category: 11,
categoryDescription: 'produce',
price: 0.69,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 0,
tprStartDate: '',
tprEndDate: '',
vendorId: 'PROD',
vendorCode: 425794,
uom: 'C',
baseCost: 0.5,
case: 1,
},
{
upc: '00000000000117',
description: 'SnapCrack Treats ',
dept: 32,
deptDescription: 'Soda/Candy',
category: 4,
categoryDescription: 'CANDY & GUM',
price: 1.1,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 1.05,
tprStartDate: '5/8/2022',
tprEndDate: '5/14/2022',
vendorId: 'ABC',
vendorCode: 325794,
uom: 'C',
baseCost: 0.99,
case: 1,
},
{
upc: '00000000000118',
description: 'Twizzlers',
dept: 32,
deptDescription: 'Soda/Candy',
category: 4,
categoryDescription: 'CANDY & GUM',
price: 2.65,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 1.89,
tprStartDate: '9/1/2022',
tprEndDate: '9/30/2022',
vendorId: 'ABC',
vendorCode: 325694,
uom: 'C',
baseCost: 1.99,
case: 1,
},
{
upc: '00000000000119',
description: 'Rolos',
dept: 32,
deptDescription: 'Soda/Candy',
category: 4,
categoryDescription: 'CANDY & GUM',
price: 1.79,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 1.69,
tprStartDate: '9/1/2022',
tprEndDate: '9/30/2022',
vendorId: 'ABC',
vendorCode: 325793,
uom: 'C',
baseCost: 1.69,
case: 1,
},
{
upc: '00000000000120',
description: 'Gummie Bears',
dept: 32,
deptDescription: 'Soda/Candy',
category: 4,
categoryDescription: 'CANDY & GUM',
price: 2.1,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 1.69,
tprStartDate: '',
tprEndDate: '',
vendorId: 'ABC',
vendorCode: 325694,
uom: 'C',
baseCost: 2.05,
case: 1,
},
{
upc: '0006414428243',
description: 'ROTEL DC TOM/GR CHL ',
dept: 1,
deptDescription: 'Grocery',
category: 3,
categoryDescription: 'BAKING NEEDS',
price: 1.49,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 1.29,
tprStartDate: '4/13/2022',
tprEndDate: '9/1/2022',
vendorId: '',
vendorCode: null,
uom: '',
baseCost: 0,
case: 1,
},
{
upc: '0006414428263',
description: 'ROTEL MLD TOM GR CHL 10 OZ',
dept: 1,
deptDescription: 'Grocery',
category: 3,
categoryDescription: 'BAKING NEEDS',
price: 1.49,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 1.29,
tprStartDate: '4/13/2022',
tprEndDate: '9/1/2022',
vendorId: '',
vendorCode: null,
uom: '',
baseCost: 0,
case: 1,
},
{
upc: '0006414428266',
description: 'ROTEL X HOT DC TOM 10 OZ',
dept: 1,
deptDescription: 'Grocery',
category: 3,
categoryDescription: 'BAKING NEEDS',
price: 1.49,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 1.29,
tprStartDate: '4/13/2022',
tprEndDate: '9/1/2022',
vendorId: '',
vendorCode: null,
uom: '',
baseCost: 0,
case: 1,
},
{
upc: '0001760008102',
description: 'CASA TACO SHELLS 4.5 OZ',
dept: 1,
deptDescription: 'Grocery',
category: 9,
categoryDescription: 'COOKIES - CRACKERS - MISCELLAN',
price: 2.19,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 0,
tprStartDate: '0',
tprEndDate: '0',
vendorId: '00050',
vendorCode: 112345,
uom: 'U',
baseCost: 1.99,
case: 1,
},
{
upc: '0001760008120',
description: 'CASA TACO DINNER 9.75 OZ',
dept: 1,
deptDescription: 'Grocery',
category: 9,
categoryDescription: 'COOKIES - CRACKERS - MISCELLAN',
price: 2.79,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 0,
tprStartDate: '0',
tprEndDate: '0',
vendorId: '00050',
vendorCode: 112344,
uom: 'U',
baseCost: 2.59,
case: 1,
},
{
upc: '0001760008122',
description: 'CASA TACO SEASONING 1.25 OZ',
dept: 1,
deptDescription: 'Grocery',
category: 9,
categoryDescription: 'COOKIES - CRACKERS - MISCELLAN',
price: 0.79,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 0,
tprStartDate: '0',
tprEndDate: '0',
vendorId: '00050',
vendorCode: 112343,
uom: 'U',
baseCost: 0.59,
case: 1,
},
{
upc: '0001760008557',
description: 'CASA TACO SALAD SEAS 1.25 OZ',
dept: 1,
deptDescription: 'Grocery',
category: 9,
categoryDescription: 'COOKIES - CRACKERS - MISCELLAN',
price: 1.69,
qty: 1,
isWeightRequired: false,
pack: 'each',
tprPrice: 0,
tprStartDate: '0',
tprEndDate: '0',
vendorId: '00050',
vendorCode: 112342,
uom: 'U',
baseCost: 1.29,
case: 1,
},
{
upc: '0020000400000',
description: 'Carnitas',
dept: 9,
deptDescription: 'Meat',
category: 6198,
categoryDescription: 'MISC SIDE MEATS',
price: 1.99,
qty: 1,
isWeightRequired: true,
pack: 'each',
tprPrice: 0,
tprStartDate: '0',
tprEndDate: '0',
vendorId: '00050',
vendorCode: 221456,
uom: 'U',
baseCost: 0.99,
case: 1,
},
{
upc: '0020000600000',
description: 'Barbacoa',
dept: 9,
deptDescription: 'Meat',
category: 6198,
categoryDescription: 'MISC SIDE MEATS',
price: 3.59,
qty: 1,
isWeightRequired: true,
pack: 'each',
tprPrice: 0,
tprStartDate: '0',
tprEndDate: '0',
vendorId: '00050',
vendorCode: 190263,
uom: 'U',
baseCost: 2.59,
case: 1,
},
];

As you can see, these are just a bunch of grocery items that you might expect to get back from an api request to a remote server.

Let’s see how we can manipulate this data. Let’s first just bring the data into a Maybe and make sure that works and then we can build on top of that. In our index.js file, let’s add this code:

import { data } from './data';

const maybeData = Maybe.just(data).extract();
console.log(maybeData);

I hope you think that’s cool, because I do, and I know what we can do with it. Your console should now look like this:

data

Now, let’s really do something with this data. If we take a look at our data, then maybe we want to extract everything in dept 32 that has a price of more than 2 dollars. Let’s take a look and see how to do that, shall we?

In our index.js, let’s add this code:

const maybeData = Maybe.just(data)
.map((x) => x.filter((i) => i.dept === 32))
.extract();
console.log(maybeData);

Sweet, now we are down to 4 items that look like this:

Now, let’s filter out the items that have a price of more than 2 dollars:

const maybeData = Maybe.just(data)
.map((x) => x.filter((i) => i.dept === 32))
.map((x) => x.filter((i) => i.price > 2))
.extract();
console.log(maybeData);

Even cooler, now we are down to 2 items. But, let’s take a step back really quickly and see what happens if we do not get data from our server. What does that look like?

const maybeData = Maybe.just(null)
.map((x) => x.filter((i) => i.dept === 32))
.map((x) => x.filter((i) => i.price > 2))
.extract();
console.log(maybeData);

Notice that we just get a null back in our console. We have run two map functions over the input to the maybe and nothing broke. If we tried to run a filter command with undefined as the source of the data to be filtered, we would have a red console. What does that look like exactly:

console.log(null.filter((i) => i.dept === 32));

Not such a great outcome. Let’s delete that line to get things right again and see what we have so far. Let’s take a look at these items:

Let’s fix this messed up price in our mapping as well. For this, we are going to need a little helper function called formatMoney. Lets go to the utils.js file and add this code:

export const formatMoney = (x) => {
x = x.toString();
const pos = x.indexOf('.');
const left = x.substring(0, pos);
let right = x.substring(pos + 1);
if (right.length === 1) {
right = right + '0';
}
return `${left}.${right}`;
};

This is not a perfect function, but it will get us by with what we need. Now we can change our code in index.js to look like this:

import { formatMoney } from './utils';

const maybeData = Maybe.just(data)
.map((x) => x.filter((i) => i.dept === 32))
.map((x) => x.filter((i) => i.price > 2))
.map((x) => x.map((i) => ({ ...i, price: formatMoney(i.price) })))
.extract();
console.log(maybeData);

Now, if we look at our items, we can see the price looks good:

That’s basically how I would use the mapping. I can easily read the function and tell exactly what it’s doing. If, for example, I though that the item had a dept and it didn’t, then normally that blows up my code, but if it’s not there, in this scenario, it’s not a problem.

I think in the next post, we are going to build a .NET core api, and see about getting our data from there. Stay tuned.

--

--