我正在使用React,但我认为这只是一个普通的JavaScript问题。在我的数据模型中,我有一个对象数组。这是一个缩短版本:
this.props.exercises = [
{
name: 'Push up',
equipment: 'none',
group: 'push'
},
{
name: 'Bench press',
equipment: 'Barbell',
group: 'push'
},
{
name: 'Pull-Up Bar',
equipment: 'Pull-Up Bar',
group: 'pull'
},
{
name: 'Dumbbell / Kettlebell Squat',
equipment: ['Dumbbell', 'Kettlebell'],
group: 'legs'
}
]
我需要对这个数组进行一些非常复杂的过滤。如果项目的名称与selectedExercise字符串匹配,则始终需要返回该字符串。如果这个条件不匹配,那么我需要以下条件:
如果用户选择了一个组,则只返回该组中的练习。
如果用户输入了搜索文本,则结果也应按此过滤。
用户指定他们拥有的设备,然后也应该过滤结果。练习不需要任何设备,1件设备,或者有一系列可能的设备选择。
我的代码可以使用,但我觉得它非常脆弱,如果出现问题,将会成为调试的噩梦。什么是更好的方法?功能性编程可以解决吗?
renderExercises() {
const {selectedType} = this.state;
const allExercises = this.props.exercises;
// If there is search text then create a var for it
let searchText = false;
if (this.searchText && this.searchText.value.length > 0) {
searchText = this.searchText.value.toLowerCase();
}
return (
allExercises
// Set the active exercise to have an active property
.map((item) => {
if (this.props.chosenExercise === item.name) {
item.active = true;
} else {
item.active = false;
}
return item;
})
.filter((item) => {
// If the exercise is active then return true
if (item.active === true) {
return true
}
// Filter by the exercise group if one is selected
if (item.group !== selectedType && selectedType !== 'all') {
return false;
}
// If there is search text then filter out non matches
if (searchText && !item.name.toLowerCase().includes(searchText)) {
return false;
}
// EQUIPMENT CONDITIONS
// If the exercise doesn't need any equipment then return true
if (item.equipment === 'none') {
return true;
}
// If the user has all equipment then return true
if (this.props.equipmentSelectionState === 'all') {
return true;
}
// If the item only has 1 piece of equipment then the type will be a string not an array
if (typeof item.equipment == 'string' || item.equipment instanceof String) {
// If the selected equipment array contains the items's required equipment then return true
if (this.props.equipmentSelection.includes(item.equipment)) {
return true;
} else {
return false;
}
} else {
let test = false;
item.equipment.forEach((itemEquipment => {
if (this.props.equipmentSelection.includes(itemEquipment)) {
test = true;
}
}));
if (test === true) {
return true;
}
}
return false;
})
// Sort by name
.sort((a, b) => {
if (a.name < b.name)
return -1;
if (a.name > b.name)
return 1;
return 0;
})
.map((item) => {
return (
<ChooseAnExercises
key={item.name}
name={item.name}
active={item.active}
setNumber={this.props.number}
updateValue={this.props.updateValue}
/>
)
})
)
}
答案 0 :(得分:0)
您可以仅使用true
条件缩小过滤器部分,并首先采用一般条件对其进行排序,然后进行更多类型的昂贵操作,例如迭代数组。
.filter(item =>
item.active ||
item.equipment === 'none' ||
this.props.equipmentSelectionState === 'all' ||
selectedType === 'all' ||
item.group === selectedType ||
searchText && item.name.toLowerCase().includes(searchText) ||
(Array.isArray(item.equipment)
? item.equipment
: [item.equipment]
).some(itemEquipment => this.props.equipmentSelection.includes(itemEquipment))
)
所有条件都使用逻辑OR链接。如有必要,将字符串转换为数组,以便使用相同的检查进行迭代。
答案 1 :(得分:0)
您必须多次过滤。试试这个简单的方法。
演示
var exercises = [{
name: 'Push up',
equipment: 'none',
group: 'push'
},
{
name: 'Bench press',
equipment: 'Barbell',
group: 'push'
},
{
name: 'Pull-Up Bar',
equipment: 'Pull-Up Bar',
group: 'pull'
},
{
name: 'Dumbbell / Kettlebell Squat',
equipment: ['Dumbbell', 'Kettlebell'],
group: 'legs'
}
];
var searchName = "Push up";
var searchGroup = "push";
var searchText = "Dumbbell";
//as per priority do the name search first.
var directSeachNameMatches = exercises.filter((s) => s.name == searchName);
//Do a group search if direct name search doesn't give anything back
var groupMatches = directSeachNameMatches.length ? directSeachNameMatches : exercises.filter((s) => s.group == searchGroup);
//Do a text search if group name search doesn't give anything back
var searchTextMatches = groupMatches.length ? groupMatches : exercises.filter(function(s) {
if (!Array.isArray(s.equipment)) {
s.equipment = [s.equipment]
};
return s.equipment.indexOf(searchText) != -1;
});
var finalMatch = searchTextMatches ;
console.log(finalMatch);
&#13;
答案 2 :(得分:0)
从一些基本的简化开始:
if (x === true)
应为if (x)
(除非您的变量可以保存不同类型的值,但实际上它不应该首先出现)if (x) return true; else return false
应为return x
test = false; x.forEach(v => { if (p(v)) test = true; })
应为test = x.some(p)
然后,更改您的数据。如前所述,您不应在同一位置使用不同类型的值,特别是equipment
不是string
,String
实例或{{1} }}。只是让它始终是一个字符串数组!
Array
,有一个空数组现在我们至少可以简化到
'none'
现在将其转换为单个布尔表达式:
return (
allExercises
// Set the active exercise to have an active property
.map((item) => {
item.active = (this.props.chosenExercise === item.name);
return item;
})
.filter((item) => {
// If the exercise is active then return true
if (item.active) {
return true
}
// Filter by the exercise group if one is selected
if (item.group !== selectedType && selectedType !== 'all') {
return false;
}
// If there is search text then filter out non matches
if (searchText && !item.name.toLowerCase().includes(searchText)) {
return false;
}
// If the exercise doesn't need any equipment then return true
if (!item.equipment.length) {
return true;
}
// If the user has all equipment then return true
if (this.props.equipmentSelectionState === 'all') {
return true;
}
return item.equipment.some(itemEquipment =>
this.props.equipmentSelection.includes(itemEquipment)
);
})
答案 3 :(得分:0)
我意识到尝试不过滤活动项目会阻止我链接过滤器,我认为这是最简单,更易读的解决方案。因此,我删除活动项并将其存储在单独的对象中,并在我完成过滤后将其添加回来。
return (
allExercises
// Set the active exercise to have an active property
.filter((item) => {
if (this.props.chosenExercise === item.name) {
item.active = true;
Object.assign(activeItem, item);
return false; // Remove the active element so we can add it later independent of filtering
} else {
item.active = false;
return true;
}
})
.filter(item => {
const {selectedType} = this.state;
if (selectedType === 'all') return true; // If 'all' exercise group is selected then let everything pass
if (item.group === selectedType) return true; // If the selected exercise group matches the exercise's group then pass
})
.filter(item => {
if (!searchText) {
return true; // If no search text let everything pass
} else if (item.name.toLowerCase().includes(searchText)) {
return true; // If there is search text then match against this
}
})
.filter(item => {
if (item.equipment === 'none') return true; // If exercise doesn't need equipment then always pass
if (this.props.equipmentSelectionState === 'all') return true; // If user has all equipment selected then always pass
// item.equipment is a string if there is only 1 piece of equipment required by the exercise
if (typeof item.equipment === 'string') {
// Check the users selected equipment for a match
if (this.props.equipmentSelection.includes(item.equipment)) {
return true;
}
} else {
// item.equipment is an array
let test = false;
this.props.equipmentSelection.forEach((equipmentSelection) => {
item.equipment.forEach(itemEquipment => {
if (itemEquipment === equipmentSelection) {
test = true;
}
});
});
return test;
}
})
// Sort by name
.concat(activeItem) // Add the active item we removed earlier
.filter((item) => {
// Filter out the empty result which is pushed when no exercise is active
if (Object.keys(item).length > 0) return true;
})
.sort((a, b) => {
// Sort alphabetically
if (a.name < b.name)
return -1;
if (a.name > b.name)
return 1;
return 0;
})
.map((item) => {
return (
<ChooseAnExercises
key={item.name}
name={item.name}
active={item.active}
setNumber={this.props.number}
updateValue={this.props.updateValue}
/>
)
})
)