Javascript 如何使用TypeScript形成灵活的可扩展过滤器?
我需要使用运算符“OR”和“and”构建一个带有TypeScript的通用过滤器。应该有一种使用范围和多选过滤器的方法。 但我是OOP新手,不知道应该如何使用类来实现。 对后端的筛选器请求应如下所示,如果存在新类型,则必须有一种方法来扩展它们:Javascript 如何使用TypeScript形成灵活的可扩展过滤器?,javascript,typescript,oop,filter,filtering,Javascript,Typescript,Oop,Filter,Filtering,我需要使用运算符“OR”和“and”构建一个带有TypeScript的通用过滤器。应该有一种使用范围和多选过滤器的方法。 但我是OOP新手,不知道应该如何使用类来实现。 对后端的筛选器请求应如下所示,如果存在新类型,则必须有一种方法来扩展它们: [ { field: 'id', operator: '=', value: 12 }, 'OR', { field: 'name', operator: '=', value: 'testName' }, 'OR', [ {
[
{ field: 'id', operator: '=', value: 12 },
'OR',
{ field: 'name', operator: '=', value: 'testName' },
'OR',
[
{ field: 'age', operator: '>=', value: '20' },
'AND',
{ field: 'age', operator: '=<', value: '30' }
]
]
FilterParameters类如下所示,但它不可扩展:
export class FilterParameters {
filter: (string | object | number)[]
constructor() {
this.filter = []
}
createFilter(receivedFields: (object)[]) {
let filterArr:(object)[] = []
let defaultFilter = receivedFields.filter((item) => item.type === 'default')
let rangeFilter = receivedFields.filter(item => item.type === 'range')
defaultFilter.forEach((item:any) => {
let defaultFilterObj = {
field: item.field,
operator: '=',
value: item.value
}
filterArr.push(defaultFilterObj)
})
rangeFilter.forEach((item:any) => {
let conditionArr = []
let startCondition = item.value.split(' ')[0]
let endCondition = item.value.split(' ').pop()
let objStartCondition = {
field: item.field,
operator: '>=',
value: startCondition
}
let objEndCondition = {
field: item.field,
operator: '=<',
value: endCondition
}
conditionArr.push(objStartCondition)
conditionArr.push('AND')
conditionArr.push(objEndCondition)
filterArr.push(conditionArr)
})
this.filter = filterArr.map((e, i) => i < filterArr.length - 1 ? [e, 'OR'] : [e]).reduce((a, b) => a.concat(b))
return this.filter
}
}
导出类过滤器参数{
过滤器:(字符串|对象|编号)[]
构造函数(){
this.filter=[]
}
createFilter(receivedFields:(对象)[]){
让filterArr:(对象)[]=[]
让defaultFilter=receivedFields.filter((项)=>item.type==='default')
让rangeFilter=receivedFields.filter(item=>item.type==='range')
defaultFilter.forEach((项目:任意)=>{
设defaultFilterObj={
字段:item.field,
运算符:'=',
值:item.value
}
filterArr.push(默认FilterObj)
})
rangeFilter.forEach((项目:任意)=>{
让条件arr=[]
让startCondition=item.value.split(“”)[0]
让endCondition=item.value.split(“”).pop()
设objStartCondition={
字段:item.field,
运算符:“>=”,
值:startCondition
}
设objEndCondition={
字段:item.field,
运算符:'=如果我从头开始设计,我会改变很多事情,因为您当前的方法有很多限制。例如,输入数组总是使用”或“
”,无法使用”和“
”或创建分组
我假设您的输入格式和输出格式是固定的,我们的任务只是创建一种从输入映射到输出的更可扩展的方式
输入的每个过滤器都是这样的,我们希望收到一个它们的数组
interface InputFilter {
field: string;
type: string;
value: any;
}
由于嵌套,输出更加复杂
interface OutputFilterElement {
field: string;
operator: string;
value: any;
}
type Joiner = 'OR' | 'AND';
type OutputFilterArray = Array<OutputFilterElement | Joiner | OutputFilterArray>
type OutputFilter = OutputFilterElement | OutputFilterArray;
我们希望我们的FilterParameters
类只知道TypeParser
接口,而不知道或关心该接口的具体实现。因此,我们可以通过将任意数量的TypeParser
对象作为参数传递给构造函数来实例化该类
该类有一个公共方法parse
,用于根据可用的类型解析器解析输入筛选器数组。它查找匹配的解析器,然后委托实际的解析
export class FilterParameters {
private readonly typeParsers: TypeParser[];
/**
* construct an isntance of the FilterParser by passing the TypeParsers to handle specific types
*/
constructor(...typeParsers: TypeParser[]) {
this.typeParsers = typeParsers;
}
/**
* helper function parses each element of the array
* might throw an error
*/
private parseOne(input: InputFilter): OutputFilter {
// find the parser for this type
const parser = this.typeParsers.find(p => p.type === input.type);
if (!parser) {
throw new Error(`no filter found for type ${input.type}`);
}
return parser.parse(input);
}
/**
* handle an array of individual filters by assuming an 'OR' relationship
*/
public parse(input: InputFilter[]): OutputFilter {
// use flatMap to insert 'OR' between elements
return input.flatMap(
(condition, i) => {
const parsed = this.parseOne(condition);
return i === 0 ? [parsed] : ['OR', parsed]
}
);
// do we want to catch errors here? or allow them to be thrown?
}
}
如果类不需要任何类型的实例,那么我认为使用类是没有意义的,因此我将创建TypeParser
的具体实现,作为实现接口的对象
const EqualityParser: TypeParser = {
type: 'default',
parse: ({ field, value }: InputFilter): OutputFilter => {
return ({
field,
// operator is always equals for default filter
operator: '=',
value
});
}
}
const RangeParser: TypeParser = {
type: 'range',
parse: ({ field, value }: InputFilter): OutputFilter => {
// split based on hyphen with optional spaces before and after
const [min, max] = value.split(/\s*-\s*/);
// are these always numbers? should we use parseFloat?
return ([{
field,
operator: '>=',
value: min
},
'AND',
{
field,
operator: '<=',
value: max
}]);
}
}
如果我从头开始设计,我会改变很多事情,因为您当前的方法有很多限制。例如,输入数组总是使用'或'
,无法使用'和'
或创建分组
我假设您的输入格式和输出格式是固定的,我们的任务只是创建一种从输入映射到输出的更可扩展的方式
输入的每个过滤器都是这样的,我们希望收到一个它们的数组
interface InputFilter {
field: string;
type: string;
value: any;
}
由于嵌套,输出更加复杂
interface OutputFilterElement {
field: string;
operator: string;
value: any;
}
type Joiner = 'OR' | 'AND';
type OutputFilterArray = Array<OutputFilterElement | Joiner | OutputFilterArray>
type OutputFilter = OutputFilterElement | OutputFilterArray;
我们希望我们的FilterParameters
类只知道TypeParser
接口,而不知道或关心该接口的具体实现。因此,我们可以通过将任意数量的TypeParser
对象作为参数传递给构造函数来实例化该类
该类有一个公共方法parse
,用于根据可用的类型解析器解析输入筛选器数组。它查找匹配的解析器,然后委托实际的解析
export class FilterParameters {
private readonly typeParsers: TypeParser[];
/**
* construct an isntance of the FilterParser by passing the TypeParsers to handle specific types
*/
constructor(...typeParsers: TypeParser[]) {
this.typeParsers = typeParsers;
}
/**
* helper function parses each element of the array
* might throw an error
*/
private parseOne(input: InputFilter): OutputFilter {
// find the parser for this type
const parser = this.typeParsers.find(p => p.type === input.type);
if (!parser) {
throw new Error(`no filter found for type ${input.type}`);
}
return parser.parse(input);
}
/**
* handle an array of individual filters by assuming an 'OR' relationship
*/
public parse(input: InputFilter[]): OutputFilter {
// use flatMap to insert 'OR' between elements
return input.flatMap(
(condition, i) => {
const parsed = this.parseOne(condition);
return i === 0 ? [parsed] : ['OR', parsed]
}
);
// do we want to catch errors here? or allow them to be thrown?
}
}
如果类不需要任何类型的实例,那么我认为使用类是没有意义的,因此我将创建TypeParser
的具体实现,作为实现接口的对象
const EqualityParser: TypeParser = {
type: 'default',
parse: ({ field, value }: InputFilter): OutputFilter => {
return ({
field,
// operator is always equals for default filter
operator: '=',
value
});
}
}
const RangeParser: TypeParser = {
type: 'range',
parse: ({ field, value }: InputFilter): OutputFilter => {
// split based on hyphen with optional spaces before and after
const [min, max] = value.split(/\s*-\s*/);
// are these always numbers? should we use parseFloat?
return ([{
field,
operator: '>=',
value: min
},
'AND',
{
field,
operator: '<=',
value: max
}]);
}
}
为什么要转换数组?如何表示[{field:'id',operator:'12}'和',{field:'name',operator:'testName'}]
或[{field:'percentage',operator:'>',value:'20.123'}和',{field:'percentage',operator:'IMHO此类筛选器的简单表示形式是二叉树,如{OR:[{field:'id',operator:'=',value:12},{OR:[{field:'name',operator:'=',value:'testName'}和:[{field:'age',operator:'>=',value:'20'},{field age:'age',operator:'=',value:'20'}]
。此筛选器的类型是接口或{Or:[filter,filter];}
,接口和{And:[filter,filter];}
,类型筛选器=或|和|{field:string;运算符:string;值:string | number;};
我需要一个数组[{field:'name',type:'default',value:'yulya'},{field:'age',type:'range',value:'20-30'}]
并将其转换为[{field:'id',运算符:'=',value:12}',OR',{field:'name',运算符:'=',value:'testName'},'或',[{field:'age',operator:'>=',value:'20'},'和',{field:'age',operator:'''=指定了优先级吗?后端对[{field:'id',operator:'=',value:12}'和',{field:'name',operator:'=',value:'testName'}或',{field age:'age',operator:'>=',value:'20}做了什么
?第一个和
或第一个或
或从左到右或从右到左?为什么要转换数组?如何表示[{字段:'id',运算符:'=',值:12}