前言
注意:这个问题与复杂性有关。我在这里使用了一个复杂的设计模式,为了理解这个问题你不需要理解。我可以更简化它,但为了防止错误,我选择保持它相对不受影响。代码是用TypeScript编写的,TypeScript是一套超级JavaScript。
代码
请注意以下课程:
export class ConcreteFilter implements Filter {
interpret() {
// rows is a very large array
return (rows: ReportRow[], filterColumn: string) => {
return rows.filter(row => {
// I've hidden the implementation for simplicity,
// but it usually returns either an empty array or a very short one.
}
}).map(row => <string>row[filterColumn]);
}
}
}
它接收一个报告行数组,然后它通过我隐藏的一些逻辑过滤数组。最后,它不返回整行,而只返回filterColumn
中提到的一个字符串列。
现在,看一下以下函数:
function interpretAnd (filters: Filter[]) {
return (rows: ReportRow[], filterColumn: string) => {
var runFilter = filters[0].interpret();
var intersectionResults = runFilter(rows, filterColumn);
for (var i=1; i<filters.length; i++) {
runFilter = filters[i].interpret();
var results = runFilter(rows, filterColumn);
intersectionResults = _.intersection(intersectionResults, results);
}
return intersectionResults;
}
}
它接收一个过滤器数组,并返回过滤器返回的所有“filterColumn”的不同数组。
在for循环中,我从每个过滤器获取结果(字符串数组),然后进行交叉操作。
问题
报告行数组很大,因此每个runFilter
操作都很昂贵(而另一方面,过滤器数组非常短)。我希望尽可能少地迭代报表行数组。此外,runFilter
操作很可能返回零结果或非常少。
解释
假设我有3个过滤器和10亿个报告行。内部iterration,即ConcreteFilter
中的迭代,将发生30亿次,即使第一次执行runFilter
返回0结果,所以我有20亿次冗余迭代。
因此,我可以在每次迭代开始时检查intersectionResults
是否为空,如果是,则打破循环。但我确信在数学上有更好的解决方案。
此外,如果返回的第一个runFIlter
exectuion说15个结果,我希望下一个exectuion只接收15个报告行的数组,这意味着我希望交集操作影响下一次调用{}的输入{1}}。
我可以在每次迭代后修改报表行数组,但是我没有看到如何以一种比现在更贵的方式来实现它。
一个好的解决方案是删除runFilter
操作,然后在每个操作中传递已经过滤的数组而不是整个数组,但是我不允许这样做,因为我不能改变结果过滤器接口的格式。
我的问题
我想获得您能想到的最佳解决方案以及解释。
非常感谢每一个愿意花时间帮助我的人。
答案 0 :(得分:1)
不确定这会有多有效,但这是您可以采取的一种可行方法。如果您通过过滤器列预处理行,则可以检索匹配的行。如果您通常拥有2个以上的过滤器,那么这种方法可能会更有益,但是它会占用大量内存。您可以根据过滤器的数量来分支方法。可能有一些TS构造更有用,不太熟悉它。下面的代码中有一些注释:
var map = {};
// Loop over every row, keep a map of rows with a particular filter value.
allRows.forEach(row => {
const v = row[filterColumn];
let items;
items = map[v] = map[v] || [];
items.push(row)
});
let rows = allRows;
filters.forEach(f => {
// Run the filter and return the unique set of matched strings
const matches = unique(f.execute(rows, filterColumn));
// For each of the matched strings, go and look up the remaining rows and concat them for the next filter.
rows = [].concat(...matches.reduce(m => map[v]));
});
// Loop over the rows that made it all the way through, extract the value and then unique() the collection
return unique(rows.map(row => row[filterColumn]));
再考虑一下,您可以使用类似的方法,但只是在每个过滤器的基础上进行:
let rows = allRows;
filters.forEach(f => {
const matches = f.execute(rows, filterColumn);
let map = {};
matches.forEach(m => {
map[m] = true;
});
rows = rows.filter(row => !!map[row[filterColumn]]);
});
return distinctify(rows.map(row => row[filterColumn]));