如何使过滤函数进一步缩小过滤范围,而不重新加载数据?
如何使过滤函数进一步缩小过滤范围,而不重新加载数据?
我有一个包含产品型号的数据集,我想通过减少数据集来使用数组过滤器进行过滤。我有一些单选输入框,它们执行过滤函数以减少数据集,但每次点击事件都会重置数据。
我希望它们能够检查是否已选择了一个单选按钮,并从那里进一步减少数据。
这是一个简要的说明:我有这样的数据:
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"
},
}
我还有一个change事件,像这样加载数据:
if (e.target.value === "strat") {
data = Object.values(stratPickups);
refreshView(data);
}
这调用了一个名为refreshView(data)的函数,它以这样的方式填充屏幕上的“卡片”:
const refreshView = (dataSet) => {
dataSet.forEach((p) => {
output.innerHTML += `
${p.name}
Specs:
Output: ${p.output}
Description: ${p.description}
`;
});
};
最后,我有一些单选输入框,它们应该基于外观和设计过滤数据数组。它看起来像这样:
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");
}
let traditionalData = data;
refreshView(traditionalData) // 使用过滤后的数据刷新视图
}
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) // 使用过滤后的数据刷新视图
}
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);
问题:上述方法可以正常工作,但每次点击时,它都会加载过滤后的数据集。它不会考虑是否选择了其他复选框并从那里进行过滤。我的问题是,您会如何设计这个过滤函数以考虑这些因素?
这里有一个CodePen:CodePen
要重现:
- 从Pickup Type中选择Stratocaster Pickups
- 点击“Unique Appearance”进行更多过滤,注意结果
- 从第二组过滤器中点击“Single Coil”,注意数据集被重新加载。
更新
我创建了一个名为filters
的对象:
const filters = {
traditional: false,
unique: false,
singleCoil: false,
humCancelling: false,
};
还添加了一个名为addEventListeners()
的函数,它控制每个对象键的true / false值。
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();
最后,我将我的函数改为Pure函数,如下所示:
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);
};
现在,我正在努力完成下一步,基于下面的评论。您会有什么不同的做法吗?
如何让过滤函数进一步筛选,而不是重新加载数据?
问题出现的原因是过滤函数具有刷新的副作用,因此它们不是纯粹的。输入是一个数组,输出是一个经过过滤的数组,不涉及任何复杂操作。
解决方法是将过滤函数拆分为纯函数,它们只接受一个列表并返回应用了该过滤器的列表。然后,创建一个"refreshWithFilters"方法,该方法将选择正确的电吉他/斯特拉特对象,然后遍历过滤器状态对象,并应用所有正确的过滤器,最后刷新视图。然后,输入事件侦听器只需切换过滤器状态对象中与其关联的布尔值,并调用该通用过滤器+刷新方法。
具体的解决方法如下:
首先,存储一个数组/对象,用于存储输入状态(复选框而不是单选按钮,以便它们不是互斥的),例如:
let filters = {single: false, singleCoil: false, unique: true ...}
然后,将过滤函数拆分为纯函数,只接受一个列表,并返回应用了该过滤器的列表,例如:
const filterByTrad = (array) => Object.values(array).filter((p) => p.look === "traditional"); const filterByUni = (array) => Object.values(array).filter((p) => p.look === "unique");
接下来,为事件侦听器创建相应的函数,这些函数将切换其关联布尔值在过滤器状态对象中的状态,并调用共享的过滤器+刷新方法,例如:
traditionalAppearance.addEventListener("click", function () { filters.traditional = this.checked; refreshWithFilters() }); uniqueAppearance.addEventListener("click", function () { filters.unique = this.checked; refreshWithFilters() });
最后,创建一个"refreshWithFilters"函数,该函数获取原始数据,应用所有过滤器以确保它们都被应用,然后刷新数据,例如:
const refreshWithFilters = () => { let data = (pickupType.value === "strat") ? stratPickups : telePickups; data = Object.values(data); if (filters.traditional) data = filterByTrad(data); if (filters.unique) data = filterByUni (data); refreshView(data); }
这样,你就有了一组可分离、可测试的过滤器,在一个大的、不重复的共享方法中有选择地应用这些过滤器,该方法与所有点击处理程序关联。
至于复选框和单选按钮的选择,出于无障碍性和用户体验的原因,应遵循惯例:单选按钮只能选择其中的一个选项,复选框可以选择多个选项。
在你的最初问题中,你说"如果已经选择了一个单选按钮,我希望它们进行检查,并进一步减少数据。"这听起来像是连续使用多个过滤器?
如果单选按钮被分组,当设置了组中的其他单选按钮时,它们将自动取消选择,这样可以减少你在代码中的工作量!在这里可以查看如何设置:[stackoverflow.com/questions/28543752/…](https://stackoverflow.com/questions/28543752)
这种方法可能有效,但如果你启用了单线圈,可能需要禁用蜂鸣器,你可以在各个更改处理程序中完成这些操作。在"refreshWithFilters"的顶部添加一个 console.log(filters)
,看看这是否适用于单选按钮。
希望这能帮到你!