Javascript 为什么我的过滤逻辑不能像预期的那样用于预测性搜索?
我正在创建一个工具,该工具从.json文件中提取属性值,并根据人们的属性值估计我所在地区的潜在税收覆盖的影响。他们应该能够通过街道地址、地块ID或所有者姓名搜索他们的财产。当他们输入时,应用程序会动态地建议潜在的匹配 我对名称字段的过滤逻辑有问题。数据的存储不一致,例如,一些记录的格式为“LEV SAMUEL T”(最后一个中间位置),其他记录的格式仅为“SAMUEL LEV”(最后一个)。我需要人们能够找到他们的财产,无论他们以什么顺序输入他们的名字,所以我使用.split(“”)在有空格时打破标准 它正在工作,但是,在输入完整名称之前,我不会得到结果 例如:记录是“NARKEWICZ DAVID”。如果我搜索“NARKEWICZ”,记录就会显示出来。但是,如果我搜索“NARK”(没有完整填写全名),则不会显示任何内容。我希望记录能显示出来,即使只是部分匹配 其他字段没有这个问题,例如“FLORENCE ROAD”将显示记录,即使我只输入“FLORE” 下面是整个app.js代码:Javascript 为什么我的过滤逻辑不能像预期的那样用于预测性搜索?,javascript,reactjs,Javascript,Reactjs,我正在创建一个工具,该工具从.json文件中提取属性值,并根据人们的属性值估计我所在地区的潜在税收覆盖的影响。他们应该能够通过街道地址、地块ID或所有者姓名搜索他们的财产。当他们输入时,应用程序会动态地建议潜在的匹配 我对名称字段的过滤逻辑有问题。数据的存储不一致,例如,一些记录的格式为“LEV SAMUEL T”(最后一个中间位置),其他记录的格式仅为“SAMUEL LEV”(最后一个)。我需要人们能够找到他们的财产,无论他们以什么顺序输入他们的名字,所以我使用.split(“”)在有空格时打
"use strict";
const e = React.createElement;
//Create CSS styles as JS objects
const containerStyles = {
width: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
alignItems: "stretch"
};
const headerStyles = {
width: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
alignItems: "center",
alignSelf: "center"
};
const headerTextStyles = {
margin: 0
};
const inputStyles = {
width: "50%",
borderRadius: "3px",
padding: "20px",
height: "15px",
alignSelf: "center"
};
const dataContainerStyles = {
width: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
alignItems: "center",
marginTop: "20px"
};
const listContainer = {
position: "relative",
zIndex: 99
};
const listStyles = {
width: "100%",
overflow: "hidden",
position: "absolute",
top: 0,
transform: "translateX(-50%)",
left: "50%",
alignSelf: "center",
boxShadow: "0px 8px 16px 0px rgba(0,0,0,0.2)",
maxHeight: "400px",
zIndex: 99,
backgroundColor: "#fff"
};
const listItemStyles = {
textAlign: "center",
color: "white",
fontWeight: "600",
padding: "10px"
};
const rowStyles = {
width: "40%",
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
alignSelf: "center"
};
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this,
args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedAddress: null,
items: [],
searchVal: "",
data: []
};
this.handleSearch = this.handleSearch.bind(this);
this.selectItem = this.selectItem.bind(this);
}
componentDidMount() {
fetch("./values.json")
.then(response => response.json())
.then(data => {
console.log("data", data);
this.setState({ data });
})
.catch(error => {
console.error(error);
});
}
selectItem(item) {
this.setState({
selectedAddress: item,
items: [],
searchVal: ""
});
}
highlightString(q, string) {
return String(string).replace(
q.toUpperCase(),
`<span style="font-weight: bold;">${q.toUpperCase()}</span>`
);
}
handleSearch(search) {
this.setState({ searchVal: search });
this.filterData(search);
}
filterData = debounce(search => {
let items = [];
if (search) {
const q = search.toLowerCase();
const ownerNameFilter = item => {
const ownerName1 = item.OWNER1.toLowerCase().split(" ");
const ownerName2 = item.OWNER2.toLowerCase().split(" ");
const fullName =
item.OWNER1.toLowerCase() + " " + item.OWNER2.toLowerCase();
//return [ownerName1, ownerName2];
const fullNameDelim =fullName.toLowerCase().split(" ");
return [fullNameDelim];
};
const addressFilter = item => {
const addressNum = item.ADDR_NUM.toLowerCase();
const fullAddress =
item.ADDR_NUM.toLowerCase() + " " + item.FULL_STR.toLowerCase();
const streetName = item.FULL_STR.toLowerCase();
const fullAddressFiltered = fullAddress.toLowerCase().split(" ");
let abbreviations = "";
if (fullAddressFiltered.includes("rd")) {
const abbreviationIndex = fullAddressFiltered.indexOf("rd");
fullAddressFiltered[abbreviationIndex] = "road";
abbreviations = fullAddressFiltered.join(" ");
}
if (fullAddressFiltered.includes("st")) {
const abbreviationIndex = fullAddressFiltered.indexOf("st");
fullAddressFiltered[abbreviationIndex] = "street";
abbreviations = fullAddressFiltered.join(" ");
}
if (fullAddressFiltered.includes("ave")) {
const abbreviationIndex = fullAddressFiltered.indexOf("ave");
fullAddressFiltered[abbreviationIndex] = "avenue";
abbreviations = fullAddressFiltered.join(" ");
}
return [streetName, addressNum, fullAddress, abbreviations];
};
items = this.state.data.reduce((acc, item) => {
const searchProps = [
...addressFilter(item),
...ownerNameFilter(item),
String(item["PROP ID"]).toLowerCase()
];
for (let prop of searchProps) {
if (!Array.isArray(prop)) {
if (prop.includes(q)) {
let markup = `
<span class="autocompleteAddress">${this.highlightString(
q,
item.ADDR_NUM
)} ${this.highlightString(q, item.FULL_STR)}</span>
<span class="autocompleteOwner">${this.highlightString(
q,
item.OWNER1
)} ${this.highlightString(
q,
item.OWNER2
)} <span style="display: block;">${this.highlightString(
q,
item["PROP ID"] || ""
)}</span></span>
`;
item.markup = markup;
acc.push(item);
break;
}
} else {
const searchTerms = search.split(" ");
let doesMatch = false;
if (searchTerms.every(term => prop.includes(term))) {
doesMatch = true;
}
if (doesMatch) {
let markup = `
<span class="autocompleteAddress">${this.highlightString(
q,
item.ADDR_NUM
)} ${this.highlightString(q, item.FULL_STR)}</span>
<span class="autocompleteOwner">${this.highlightString(
q,
item.OWNER1
)} ${this.highlightString(
q,
item.OWNER2
)} <span style="display: block;">${this.highlightString(
q,
item["PROP ID"] || ""
)}</span></span>
`;
item.markup = markup;
acc.push(item);
break;
}
}
}
return acc;
}, []);
}
this.setState({
items: items
});
}, 500);
formatNumber(num) {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
}).format(num);
}
render() {
return (
<div id="container" style={containerStyles}>
<div id="title" style={headerStyles}>
<img className="logo" src="logo.png" />
<p style={headerTextStyles}> City of Northampton</p>
<p style={headerTextStyles}>Tax Override Calculator</p>
</div>
<input
value={this.state.searchVal}
onChange={e => this.handleSearch(e.target.value)}
style={inputStyles}
placeholder="Enter an address or parcel ID..."
/>
{this.state.items.length !== 0 && (
<div style={listContainer}>
<div style={listStyles}>
{this.state.items.map((item, index) => (
<p
dangerouslySetInnerHTML={{ __html: item.markup }}
className="autocomplete-hover"
key={item["PROP ID"] || index}
onClick={() => this.selectItem(item)}
style={listItemStyles}
/>
))}
</div>
</div>
)}
<div style={dataContainerStyles}>
<div style={rowStyles}>
<p className="label"> Parcel ID:</p>
{this.state.selectedAddress ? (
<p>{`${this.state.selectedAddress["PROP ID"]}`}</p>
) : (
<p>None Selected</p>
)}
</div>
<div style={rowStyles}>
<p className="label"> Address:</p>
{this.state.selectedAddress ? (
<p>{`${this.state.selectedAddress["ADDR_NUM"]} ${
this.state.selectedAddress["FULL_STR"]
}`}</p>
) : (
<p>None Selected</p>
)}
</div>
<div style={rowStyles}>
<p className="label">Owner:</p>
{this.state.selectedAddress ? (
<p>{`${this.state.selectedAddress["OWNER1"]} ${
this.state.selectedAddress["OWNER2"]
}`}</p>
) : (
<p>None Selected</p>
)}
</div>
<div style={rowStyles}>
<p className="label">2020 Assessed Value:</p>
{this.state.selectedAddress ? (
<p>{`${this.formatNumber(
parseFloat(this.state.selectedAddress["TOTAL VAL"])
)}`}</p>
) : (
<p>None Selected</p>
)}
</div>
<div style={rowStyles}>
<p className="label">Total Annual Impact:</p>
{this.state.selectedAddress ? (
<p>{`${this.formatNumber(
(parseFloat(
this.state.selectedAddress["TOTAL VAL"].replace(/,/g, "")
) /
1000) *
0.67
)}`}</p>
) : (
<p>None Selected</p>
)}
</div>
<div style={rowStyles}>
<p className="label">Total Quarterly Impact:</p>
{this.state.selectedAddress ? (
<p>{`${this.formatNumber(
(parseFloat(
this.state.selectedAddress["TOTAL VAL"].replace(/,/g, "")
) /
4000) *
0.67
)}`}</p>
) : (
<p>None Selected</p>
)}
</div>
</div>
</div>
);
}
}
const domContainer = document.querySelector("#root");
ReactDOM.render(<App />, domContainer);
“严格使用”;
const e=React.createElement;
//将CSS样式创建为JS对象
常量集装箱样式={
宽度:“100%”,
显示:“flex”,
flexDirection:“列”,
justifyContent:“灵活启动”,
对齐项目:“拉伸”
};
const headerStyles={
宽度:“100%”,
显示:“flex”,
flexDirection:“列”,
justifyContent:“灵活启动”,
对齐项目:“中心”,
自我定位:“中心”
};
常量headerTextStyles={
保证金:0
};
常量输入样式={
宽度:“50%”,
边界半径:“3px”,
填充:“20px”,
高度:“15px”,
自我定位:“中心”
};
常量dataContainerStyles={
宽度:“100%”,
显示:“flex”,
flexDirection:“列”,
justifyContent:“灵活启动”,
对齐项目:“中心”,
玛金托普:“20px”
};
常量listContainer={
职位:“相对”,
zIndex:99
};
常量列表样式={
宽度:“100%”,
溢出:“隐藏”,
位置:“绝对”,
排名:0,
转换:“translateX(-50%)”,
左:“50%”,
对准自己:“居中”,
盒子阴影:“0px 8px 16px 0px rgba(0,0,0,0.2)”,
最大高度:“400px”,
zIndex:99,
背景颜色:“fff”
};
常量listItemStyles={
textAlign:“居中”,
颜色:“白色”,
重量:“600”,
填充:“10px”
};
常量行样式={
宽度:“40%”,
显示:“flex”,
flexDirection:“行”,
辩护内容:“间隔空间”,
对齐项目:“中心”,
自我定位:“中心”
};
函数解盎司(函数、等待、立即){
var超时;
返回函数(){
var context=this,
args=参数;
var later=function(){
超时=空;
如果(!immediate)函数应用(上下文,参数);
};
var callNow=立即&&!超时;
clearTimeout(超时);
超时=设置超时(稍后,等待);
if(callNow)funct.apply(上下文,参数);
};
}
类应用程序扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
selectedAddress:null,
项目:[],
searchVal:“,
数据:[]
};
this.handleSearch=this.handleSearch.bind(this);
this.selectItem=this.selectItem.bind(this);
}
componentDidMount(){
获取(“./values.json”)
.then(response=>response.json())
。然后(数据=>{
控制台日志(“数据”,数据);
this.setState({data});
})
.catch(错误=>{
控制台错误(error);
});
}
选择项目(项目){
这是我的国家({
所选地址:项,
项目:[],
searchVal:“
});
}
highlightString(q,string){
返回字符串(字符串)。替换(
q、 toUpperCase(),
`${q.toUpperCase()}`
);
}
handleSearch(搜索){
this.setState({searchVal:search});
此.filterData(搜索);
}
filterData=debounce(搜索=>{
设项目=[];
如果(搜索){
const q=search.toLowerCase();
const ownerNameFilter=项目=>{
const ownerName1=item.OWNER1.toLowerCase().split(“”);
const ownerName2=item.OWNER2.toLowerCase().split(“”);
常量全名=
item.OWNER1.toLowerCase()+“”+item.OWNER2.toLowerCase();
//返回[ownerName1,ownerName2];
const fullNameDelim=fullName.toLowerCase().split(“”);
返回[fullNameDelim];
};
const addressFilter=项目=>{
const addressNum=item.ADDR_NUM.toLowerCase();
常量完整地址=
item.ADDR_NUM.toLowerCase()+“”+item.FULL_STR.toLowerCase();
const streetName=item.FULL_STR.toLowerCase();
const fullAddressFiltered=fullAddress.toLowerCase().split(“”);
让缩写为“”;
if(fullAddressFiltered.includes(“rd”)){
const abbreviationIndex=fullAddressFiltered.indexOf(“rd”);
fullAddressFiltered[abbreviationIndex]=“道路”;
缩写=fullAddressFiltered.join(“”);
}
if(fullAddressFiltered.includes(“st”)){
const abbreviationIndex=fullAddressFiltered.indexOf(“st”);
fullAddressFiltered[abbreviationIndex]=“street”;
缩写=fullAddressFiltered.join(“”);
}
if(fullAddressFiltered.includes(“ave”)){
const abbreviationIndex=fullAddressFiltered.indexOf(“ave”);
fullAddressFiltered[abbreviationIndex]=“avenue”;
缩写=fullAddressFiltered.join(“”);
}
return[街道名称、地址编号、完整地址、缩写];
};
items=此.state.data.reduce((acc,item)=>{
const searchProps=[
…地址过滤器(项目),
…所有者名称过滤器(项目),
字符串(项目[“属性ID]”)。toLowerCase()
];
for(让搜索道具中的道具){
如果(!Array.isArray(prop)){
如果(项目包括(q)){
让标记=