I have a dataset containing product models, and I want to make a filter that filters through them by reducing the data set based on array filters. I have some radio inputs that perform a filter function to reduce the data set, but they reset the data on each click event.
I want them to check if one radio button has already been selected, and further reduce the data from there.
Here's a breakdown: I have data like this:
const stratPickups = {
"Model 1": {
name: "Clean Model",
design: "Single Coil",
output: 4,
look: "traditional"
},
"Model 2": {
name: "Balanced Model",
design: "Single Coil",
output: 5,
look: "traditional"
},
"Model 3": {
name: "Balanced Model 2",
design: "Single Coil",
output: 6,
look: "traditional"
},
"Model 4": {
name: "High Output Model",
design: "Single Coil",
output: 8,
look: "traditional"
},
}
I also have a change event that loads the data like this:
if (e.target.value === "strat") {
data = Object.values(stratPickups);
refreshView(data);
Which calls a function refreshView(data) which populates the screen with "cards" like so:
const refreshView = (dataSet) => {
dataSet.forEach((p) => {
output.innerHTML += `
<div class="card pickup-result" data-output="${p.output}" data-tone="${p.tone}">
<div class="card-body">
<div class="card-heading">
<h4>${p.name}</h4>
</div>
<hr>
<h5>Specs:</h5>
<p><strong>Output: </strong> ${p.output}</p>
<p><strong>Description:</strong> ${p.description}</p>
<div class="pickup-info">
<a class="link-button" target="blank" href="${p.url}">See Product</a>
</div>
</div>
</div>
`;
});
};
Lastly, I have radio inputs that are supposed to filter the array of data based on appearance and design. It looks like this:
const filterByTrad = () => {
if (pickupType.value === "strat") {
data = Object.values(stratPickups).filter((p) => p.look === "traditional");
} else if (pickupType.value === "tele") {
data = Object.values(telePickups).filter((p) => p.look === "traditional");
}
// There are a lot more value types to be loaded.
let traditionalData = data;
refreshView(traditionalData) // Refresh the view with the filtered data
}
const filterByUni = () => {
if (pickupType.value === "strat") {
data = Object.values(stratPickups).filter((p) => p.look === "unique");
} else if (pickupType.value === "tele") {
data = Object.values(telePickups).filter((p) => p.look === "unique");
} ...
let uniqueData = data;
refreshView(uniqueData) // Refresh the view with the filtered data
}
const filterBySingleCoil = () => {
if (pickupType.value === "strat") {
data = Object.values(stratPickups).filter((p) => p.design === "single-coil");
} else if (pickupType.value === "tele") {
data = Object.values(telePickups).filter((p) => p.design === "single-coil");
}
let singleCoilData = data
refreshView(singleCoilData)
}
const filterByHumCancelling = () => {
if (pickupType.value === "strat") {
data = Object.values(stratPickups).filter((p) => p.design === "hc");
} else if (pickupType.value === "tele") {
data = Object.values(telePickups).filter((p) => p.design === "hc");
}
let hcData = data
refreshView(hcData)
}
traditionalAppearance.addEventListener("click", filterByTrad);
uniqueAppearance.addEventListener("click", filterByUni);
singleCoil.addEventListener("click", filterBySingleCoil);
hc.addEventListener("click", filterByHumCancelling);
Problem: The above works, but on every click, it loads the filtered data set. It DOES NOT take into account if any other checkboxes are selected and filter from there. My question is, how would you structure this filter function to take these into account?
Here's a CodePen:CodePen
To reproduce:
- Click Stratocaster Pickups from Pickup Type
- Click "Unique Appearance" to filter more, notice result
- Click "Single Coil" from second set of filters and notice that the dataset gets reloaded.
Update
I have created an object called filters
:
const filters = {
traditional: false,
unique: false,
singleCoil: false,
humCancelling: false,
};
Also added a function called addEventListeners()
which controls the true / false value of each object key.
const addEventListeners = () => {
traditionalAppearance.addEventListener("click", function () {
filters.traditional = this.checked;
if (this.checked == true) {
filterByTrad(data);
}
});
uniqueAppearance.addEventListener("click", function () {
filters.unique = this.checked;
if (this.checked == true) {
filterByUni(data);
}
});
singleCoil.addEventListener("click", function () {
filters.singleCoil = this.checked;
if (this.checked == true) {
filterBysingleCoil(data);
}
});
hc.addEventListener("click", function () {
filters.humCancelling = this.checked;
if (this.checked == true) {
filterByHumCancelling(data);
}
});
};
addEventListeners();
Lastly, I have made my functions Pure like so:
const filterByTrad = (array) => {
data = Object.values(array).filter((p) => p.look === "traditional");
refreshWithFilters(data);
return data;
};
const filterByUni = (array) => {
data = Object.values(array).filter((p) => p.look === "unique");
refreshWithFilters(data);
};
const filterBysingleCoil = (array) => {
data = Object.values(array).filter((p) => p.design === "single-coil");
refreshWithFilters(data);
};
const filterByHumCancelling = (array) => {
data = Object.values(array).filter((p) => p.design === "hc");
refreshWithFilters(data);
};
const refreshWithFilters = (data) => {
refreshView(data);
};
Now I'm struggling with the next steps, based on the comment below. Is there anytihng you would do differently?