I am currently trying to filter available products based on their selected options.
const products = [{
id: 1,
name: 'Safari',
horsepowers: 30,
doors: 4,
gear_type: 'automatic',
wheels: 6
},
{
id: 2,
name: 'Jungle',
horsepowers: 50,
doors: 3,
gear_type: 'automatic',
wheels: 5
},
{
id: 3,
name: 'Moon',
horsepowers: 30,
doors: 4,
gear_type: 'manual',
wheels: 4
}
]
const selectedOptions =
{
horsepowers: 50,
doors: 3,
gear_type: null,
wheels: null
}
Typically I would do something like
const availableProducts = products.filter((product) =>
product.horsepowers === selectedOptions.horsepowers &&
product.doors === selectedOptions.doors .... etc
however, how do I skip null values, empty arrays, and undefined values if the user has not yet selected all possible options yet?
CodePudding user response:
The next provided approach takes advantage of the 2nd thisArg
argument of almost every available prototypal array method.
Thus one can write a generic filter function which compares any item's property values to the related ones configured by the selectedOptions
object which will be passed alongside the filter function as filter
's 2nd argument and as the filter function's this
context ...
const selectedOptions = {
horsepowers: 50,
doors: 3,
gear_type: null,
wheels: null,
};
const products = [{
id: 1,
name: 'Safari',
horsepowers: 30,
doors: 4,
gear_type: 'automatic',
wheels: 6,
}, {
id: 2,
name: 'Jungle',
horsepowers: 50,
doors: 3,
gear_type: 'automatic',
wheels: 5,
}, {
id: 3,
name: 'Moon',
horsepowers: 30,
doors: 4,
gear_type: 'manual',
wheels: 4,
}];
function doItemPropertiesEqualEveryBoundSelectedOption(item) {
return Object
// create key value pairs from the `this` bound selected options.
.entries(this)
// skip/ignore selected option entries where `value` equals `null`.
.filter(([key, value]) => value !== null)
// execute item specific selected option validation via `every`.
.every(([key, value]) => item[key] === value);
}
console.log(
products
.filter(
doItemPropertiesEqualEveryBoundSelectedOption,
selectedOptions,
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
In order to answer another of the OP's questions ...
"however, how do I skip null values, empty arrays, and undefined values if the user has not yet selected all possible options yet?"
... and also provide a generic solution to it, the above approach can be changed to a thisArg
object which not only features the selected options but also the condition of not to be validated (invalid) selectedOption
properties ...
const products = [{
id: 1,
name: 'Safari',
horsepowers: 30,
doors: 4,
gear_type: 'automatic',
wheels: 6,
}, {
id: 2,
name: 'Jungle',
horsepowers: 50,
doors: 3,
gear_type: 'automatic',
wheels: 5,
}, {
id: 3,
name: 'Moon',
horsepowers: 30,
doors: 4,
gear_type: 'manual',
wheels: 4,
}];
const selectedOptions = {
horsepowers: 50,
doors: 3,
gear_type: null,
wheels: null,
};
const isInvalidValue = (value) => {
return Array.isArray(value)
// empty array validation.
? (value.length === 0)
// undefined and null value validation.
: (value == null)
}
function doItemPropertiesEqualEveryBoundValidOption(item) {
const { options, isInvalidValue } = this;
return Object
.entries(options)
.filter(([key, value]) => !isInvalidValue(value))
.every(([key, value]) => item[key] === value);
}
console.log(
products
.filter(
doItemPropertiesEqualEveryBoundValidOption,
{ options: selectedOptions, isInvalidValue },
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
selectedOptions.horsepowers == null ? true : product.horsepowers === selectedOptions.horsepowers &&
product.doors == null ? true : product.doors === selectedOptions.doors
If you want to keep it in line, you can use a ternary operator to check if it's null before comparing.
CodePudding user response:
You could preprocess selectedOptions
first and then comparing:
const applicableOptions = Object.entries(selectedOptions).filter(
([_, value]) => value !== null && value !== undefined
);
const availableProducts = products.filter((product) =>
applicableOptions.every(([optKey, optValue]) => product[optKey] === optValue)
);
to compare using array, you would need to update your example as there's no array property
CodePudding user response:
Rather than typing in each option by hand, you could just iterate over selectedOptions
. Then it's as simple as checking if the value of each option is null before comparing.
let filtered = products.filter(e => {
for(let [key, value] of Object.entries(selectedOptions))
{
if(value != null && e[key] != value)
return false;
}
return true;
});
const products = [{
id: 1,
name: 'Safari',
horsepowers: 30,
doors: 4,
gear_type: 'automatic',
wheels: 6
},
{
id: 2,
name: 'Jungle',
horsepowers: 50,
doors: 3,
gear_type: 'automatic',
wheels: 5
},
{
id: 3,
name: 'Moon',
horsepowers: 30,
doors: 4,
gear_type: 'manual',
wheels: 4
}
]
const selectedOptions =
{
horsepowers: 50,
doors: 3,
gear_type: null,
wheels: null
}
let filtered = products.filter(e => {
for(let [key, value] of Object.entries(selectedOptions))
{
if(value != null && e[key] != value)
return false;
}
return true;
});
console.log(filtered);
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
However, if you really want to write it out, I'd just check if the option is set with a simple boolean check. !(null)
returns true, so this would work.
return (!selectedOptions.horsepowers || selectedOptions.horsepowers == product.horsepowers) && ...
CodePudding user response:
You could generate a filter array from selectedOptions
and filter the entries and use this for filtering the data, later.
const
products = [{ id: 1, name: 'Safari', horsepowers: 30, doors: 4, gear_type: 'automatic', wheels: 6 }, { id: 2, name: 'Jungle', horsepowers: 50, doors: 3, gear_type: 'automatic', wheels: 5 }, { id: 3, name: 'Moon', horsepowers: 30, doors: 4, gear_type: 'manual', wheels: 4 }],
selectedOptions = { horsepowers: 50, doors: 3, gear_type: null, wheels: null },
filter = Object
.entries(selectedOptions)
.filter(([, v]) => v !== null),
result = products.filter(o => filter.every(([k, v]) => o[k] === v));
console.log(result);
<iframe name="sif4" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
- Using
Object#entries
andArray#filter
, get the pairs with selected values fromselectedOptions
to use for filtering the products list - Using
Array#filter
andArray#every
, filter the list to make sure that resulting products match the above pairs
const
products = [ { id: 1, name: 'Safari', horsepowers: 30, doors: 4, gear_type: 'automatic', wheels: 6 }, { id: 2, name: 'Jungle', horsepowers: 50, doors: 3, gear_type: 'automatic', wheels: 5 }, { id: 3, name: 'Moon', horsepowers: 30, doors: 4, gear_type: 'manual', wheels: 4 } ],
selectedOptions = { horsepowers: 50, doors: 3, gear_type: null, wheels: null };
const filterOptions =
Object.entries(selectedOptions).filter(([_, value]) => value !== null);
const selectedProducts =
products.filter(product =>
filterOptions.every(([key, value]) => product[key] === value)
);
console.log(selectedProducts);
<iframe name="sif5" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>