Answer:
You can create an Inventory Constructor that allows for you to parse your data however you'd like and in as many ways as you'd like.
code:
function Inventory( from ) {
return {
data: from,
getBy: function( type ) {
if ( ![ "code", "size", "quantity" ].includes( type.toLowerCase() ) ) throw new Error( "Incorrect Inventory::getBy Type" );
return function( value ) {
return Inventory(
from.filter( ( [ code, size, quantity ] ) =>
new Function( "code, size, quantity", `return ${type} === '${value.toUpperCase()}'` )( code, size, quantity )
)
);
}
}
}
}
Example:
let data={splits:[["FOG","L","0"],["FOG","M","0"],["FOG","XL","-1"],["FOG","XXXL","2"],["NVY","M","0"],["NVY","L","0"],["NVY","S","0"]]};
function Inventory(from) {
return {
data: from,
getBy: function(type) {
if (!["code", "size", "quantity"].includes(type.toLowerCase())) throw new Error("Incorrect Inventory::getBy Type");
return function(value) {
return Inventory(from.filter(([code, size, quantity]) => new Function("code, size, quantity", `return ${type} === '${value.toUpperCase()}'`)(code, size, quantity)));
}
}
}
}
let Inv = Inventory(data.splits),
noQuantity = Inv.getBy("quantity")("0").data;
console.log("Items with 0 Quantity: ");
console.log(noQuantity);
Why's this answer so verbose?
On the surface this probably seems like overkill, and in fact it may be. I know there are other fine answers, but I really felt they were lacking in optimal use.
Why?
- They don't allow nested searches
- They don't provide maintainability and reusability
This means that though they may answer your question, they're mostly not adaptable, which is important to all code as needs mutate, evolve, and edge cases need to be dealt with.
My answer provides all of the above, with very little code, and the explanation below ( if you choose to read it ) should give you a thorough explanation of how it works.
Explanation: How does this work?
We'll start from the beginning and create a simple Constructor.
Let's call it Inventory because that seems to be what your data references.
function Inventory(from) {
return {
}
}
The first thing we need to do is to store the data that we receive in the function. This can be done like so:
function Inventory(from) {
return {
data: from
}
}
This means that when we call the constructor with our Inventory data:
let Inv = Inventory(data.splits);
Within Inv we now have:
{
data: [ Inventory Data ]
}
Example:
let data={splits:[["FOG","L","0"],["FOG","M","0"],["FOG","XL","-1"],["FOG","XXXL","2"],["NVY","M","0"],["NVY","L","0"],["NVY","S","0"]]};
function Inventory(from) {
return {
data: from
}
}
let Inv = Inventory(data.splits);
console.log(Inv);
To manipulate this data in a way where we can dig into it and get our requested results, we can actually use a filter method on our array.
As an example we can adjust our constructor to look for specific quantities like this:
function Inventory(from) {
return {
data: from,
getByQuantity: function(quantity) {
return from.filter(([, , q]) => quantity === q);
}
}
}
Example:
let data={splits:[["FOG","L","0"],["FOG","M","0"],["FOG","XL","-1"],["FOG","XXXL","2"],["NVY","M","0"],["NVY","L","0"],["NVY","S","0"]]};
function Inventory(from) {
return {
data: from,
getByQuantity: function(quantity) {
return from.filter(([, , q]) => quantity === q);
}
}
}
let Inv = Inventory(data.splits),
noQty = Inv.getByQuantity("0");
console.log( noQty );
This is all well and good.
But what if we need to get all of the data with the code NVY?
Or what if we need to get all of the data with the quantity 1 within the code FOG ?
Our current pattern requires a lot of boilerplate code to generate anything more than our specified quantity result!
How do we mitigate our code and bolster functionality?
Templating Pattern:
To be more useful we can utilize a Templating Pattern combined with our filter to provide us with the ability to get any result from our data and then continue to drill down until we've gotten exactly what we need.
We can do this by adjusting our constructor. I'll walk through the steps to make it easier to understand.
Step 1:
- First we change our method to
getBy to make it variable in use.
- We provide a
type parameter, that is checked to make sure it's valid
code:
function Inventory(from) {
return {
data: from,
getBy: function(type) {
if (!["code", "size", "quantity"].includes(type.toLowerCase())) throw new Error("Incorrect Inventory::getBy Type");
}
}
}
Step 2:
- We can then return a new function that takes a
value
- This
value is what we'll check against our type while filtering
code:
function Inventory(from) {
return {
data: from,
getBy: function(type) {
if (!["code", "size", "quantity"].includes(type.toLowerCase())) throw new Error("Incorrect Inventory::getBy Type");
return function(value) {
}
}
}
Step 3:
- We can then create a Function that will
Template our type and value into a conditional, and return a boolean (true/false) of whether or not it matches.
- By passing this function into our
filter we can search for anything
code:
function Inventory(from) {
return {
data: from,
getBy: function(type) {
if (!["code", "size", "quantity"].includes(type.toLowerCase())) throw new Error("Incorrect Inventory::getBy Type");
return function(value) {
return from.filter(([code, size, quantity]) =>
new Function("code, size, quantity", `return ${type} === '${value.toUpperCase()}'`)(code, size, quantity)
);
}
}
}
}
Re-usability aside this is enough to provide a one-time search of your data based on any parameter, be that code, size, or quantity
Example:
let data={splits:[["FOG","L","0"],["FOG","M","0"],["FOG","XL","-1"],["FOG","XXXL","2"],["NVY","M","0"],["NVY","L","0"],["NVY","S","0"]]};
function Inventory(from) {
return {
data: from,
getBy: function(type) {
if (!["code", "size", "quantity"].includes(type.toLowerCase())) throw new Error("Incorrect Inventory::getBy Type");
return function(value) {
return from.filter(([code, size, quantity]) =>
new Function("code, size, quantity", `return ${type} === '${value.toUpperCase()}'`)(code, size, quantity)
);
}
}
}
}
let Inv = Inventory(data.splits);
console.log(" FOG Code: ");
console.log( Inv.getBy("code")("FOG") );
console.log(" Size Medium: ");
console.log( Inv.getBy("size")("M") );
In practice as far as your question goes, you do get your result. BUT you'll notice that it's difficult to get all Medium sizes within NVY - though it is possible. It would look something like:
let allMNVY = Inventory(
Inventory(data.splits)
.getBy("code")("NVY")
)
.getBy("size")("M");
This is not ideal.
How can we fix this?
This last step allows us to do multiple searches within our data to continuously drill down until we get the data we want.
This is done through Recursion.
Step 4:
To retain the ability to continue searching, we want to return an Inventory Constructor with the returned data as the parameter.
code:
function Inventory(from) {
return {
data: from,
getBy: function(type) {
if (!["code", "size", "quantity"].includes(type.toLowerCase())) throw new Error("Incorrect Inventory::getBy Type");
return function(value) {
return Inventory(from.filter(([code, size, quantity]) => new Function("code, size, quantity", `return ${type} === '${value.toUpperCase()}'`)(code, size, quantity)));
}
}
}
}
This constructor then allows us to continue to dig through our data, in very few lines of code. The caveat is that when we finish our search, we must look at the data property.
Our previous example of all Medium sizes within code NVY:
let allMNVY = Inventory(
Inventory(data.splits)
.getBy("code")("NVY")
)
.getBy("size")("M");
Our new example of all Medium sizes within code NVY:
let allMNVY = Inventory(data.splits)
.getBy("code")("NVY")
.getBy("size")("M")
.data;
As you can see it's much more sensible because we know exactly what occurs in this example.
- We turn
data.splits into our inventory
- We get all the
items with code NVY
- We get all the
items with size M
- We get the
data instead of making another search.
Final Solution:
function Inventory(from) {
return {
data: from,
getBy: function(type) {
if (!["code", "size", "quantity"].includes(type.toLowerCase())) throw new Error("Incorrect Inventory::getBy Type");
return function(value) {
return Inventory(from.filter(([code, size, quantity]) => new Function("code, size, quantity", `return ${type} === '${value.toUpperCase()}'`)(code, size, quantity)));
}
}
}
}
let noQty = Inventory(data.splits).getBy("quantity")("0");
Final Example:
let data={splits:[["FOG","L","0"],["FOG","M","0"],["FOG","XL","-1"],["FOG","XXXL","2"],["NVY","M","0"],["NVY","L","0"],["NVY","S","0"]]};
function Inventory(from) {
return {
data: from,
getBy: function(type) {
if (!["code", "size", "quantity"].includes(type.toLowerCase())) throw new Error("Incorrect Inventory::getBy Type");
return function(value) {
return Inventory(from.filter(([code, size, quantity]) => new Function("code, size, quantity", `return ${type} === '${value.toUpperCase()}'`)(code, size, quantity)));
}
}
}
}
let Inv = Inventory(data.splits),
noQuantity = Inv.getBy("quantity")("0").data,
allFog = Inv.getBy("code")("FOG").data,
allNvyNoQuantity = Inv.getBy("code")("NVY").getBy("quantity")("0").data;
console.log("noQuantity : " + noQuantity);
console.log("allFog : " + allFog);
console.log("allNvyNoQuantity : " + allNvyNoQuantity);
Edit after thinking about this a while, I decided to add demo code for a comparison instead of exact matching
Note Because of the number of examples within this answer, StackOverflow doesn't seem to be rendering the results of the console.log in the following examples. You may need to open your own console to verify, but they do work!
Extending Functionality: Providing a filterBy Comparison
The last thing you may want to consider instead of explicit matching, is a comparison operation to filter fluid matches.
With our above code if we want to grab the following:
- quantity is "0"
- quantity is "1"
We would need to look twice at the data. Once for "0" and again for "1".
To adjust this we can provide the ability to make a comparison. This way we can simply say:
- quantity is greater than 0
We do this by adjusting our constructor very slightly and adding a new method called filterBy:
filterBy: function( comparison ) {
return Inventory( from.filter( ( [ code, size, quantity ] ) => new Function( "code, size, quantity", `return ${comparison};`)( code, size, quantity ) ) );
}
It is almost exactly the same as our prior Templating Function, except this method will take a string and use it to compare against our data set.
Example:
let data={splits:[["FOG","L","0"],["FOG","M","0"],["FOG","XL","-1"],["FOG","XXXL","2"],["NVY","M","0"],["NVY","L","0"],["NVY","S","0"]]};
function Inventory( from ) {
return {
data: from,
getBy: function( type ) {
if ( ![ "code", "size", "quantity" ].includes( type.toLowerCase() ) ) throw new Error( "Incorrect Inventory::getBy Type" );
return function( value ) {
return Inventory( from.filter( ( [ code, size, quantity ] ) => new Function( "code, size, quantity", `return ${type} === '${value.toUpperCase()}'` )( code, size, quantity ) ) );
}
},
filterBy: function( comparison ) {
return Inventory( from.filter( ( [ code, size, quantity ] ) => new Function( "code, size, quantity", `return ${comparison};`)( code, size, quantity ) ) );
}
}
}
let Inv = Inventory(data.splits),
negativeQuantity = Inv.filterBy("Number(quantity) < 0").data;
positiveQuantity = Inv.filterBy("Number(quantity) > 0").data;
console.log("Negative Quantity:");
console.log(negativeQuantity);
console.log("Positive Quantity:");
console.log(positiveQuantity);
Additionally it also has access to any and all of the types ( code, quantity, and size )
Note In our comparison string all of our types (code, quantity, and size) are strings, and all lowercase. That is why we use Number(quantity) and not just quantity in the above example.
A huge bonus of our Templating Pattern is that we can write any JavaScript comparison and it will work. Going back to our code NVY and size M example, we can now write:
let MNVY = Inv.filterBy("code == 'NVY' && size == 'M'").data;
Example:
let data={splits:[["FOG","L","0"],["FOG","M","0"],["FOG","XL","-1"],["FOG","XXXL","2"],["NVY","M","0"],["NVY","L","0"],["NVY","S","0"]]};
function Inventory( from ) {
return {
data: from,
getBy: function( type ) {
if ( ![ "code", "size", "quantity" ].includes( type.toLowerCase() ) ) throw new Error( "Incorrect Inventory::getBy Type" );
return function( value ) {
return Inventory( from.filter( ( [ code, size, quantity ] ) => new Function( "code, size, quantity", `return ${type} === '${value.toUpperCase()}'` )( code, size, quantity ) ) );
}
},
filterBy: function( comparison ) {
return Inventory( from.filter( ( [ code, size, quantity ] ) => new Function( "code, size, quantity", `return ${comparison};`)( code, size, quantity ) ) );
}
}
}
let Inv = Inventory(data.splits),
MNVY = Inv.filterBy("code == 'NVY' && size == 'M'").data;
console.log("NVY and M : " + MNVY);
Conclusion:
Though a bit lengthy, I hope this helps!
Happy coding!
somepart ^_^ Good that you mentioned no output is expected :D