javascript - 如何优化下面的过滤器逻辑以仅使用 1 个循环而不是 2 个?
问题描述
JSfiddle 代码:https ://jsfiddle.net/bryanh210/j8yz2sep/3/
我目前有这段代码,并认为该解决方案非常麻烦,并想问是否有另一种方法可以使代码更有效地在 1 个循环中执行。
const groupItemByCategories = (arr) => {
blockMenuEl.innerHTML = '';
const cateArr = arr.map(item => item.categoryLabel);
cateArr.forEach(cate => {
const filteredArr = arr.filter(arr => arr.categoryLabel === cate);
displayAccordingToCategories(filteredArr, cate);
})
}
在上面的代码中,我首先获取所有categoryLabel
元素,然后遍历下面的原始数组(块),并调用一个函数将所有项目显示到页面上。
这是作为参数传入的数组:
const blocks = [
{
label: 'Burger',
categoryId: 'burgers',
categoryLabel: 'Burgers',
tags: [ 'burger', 'lettuce', 'tomato' ]
}, {
label: 'Cheese Burger',
categoryId: 'burgers',
categoryLabel: 'Burgers',
tags: [ 'cheese', 'burger', 'lettuce', 'tomato' ]
}, {
label: 'Veggie Burger',
categoryId: 'burgers',
categoryLabel: 'Burgers',
tags: [ 'vegetable', 'burger', 'lettuce', 'tomato' ]
}, {
label: 'Fancy Burger',
categoryId: 'burgers',
categoryLabel: 'Burgers',
tags: [ 'cheese', 'burger', 'lettuce', 'tomato', 'fancy sauce' ]
}, {
label: 'Cheese Fries',
categoryId: 'fries',
categoryLabel: 'Fries',
tags: [ 'fries', 'cheese', 'potato', 'fried' ]
}, {
label: 'Shake',
categoryId: 'dessert',
categoryLabel: 'Dessert',
tags: [ 'shake', 'milk', 'chocolate', 'strawberry', 'frozen', 'dessert', 'sweet' ]
}, {
label: 'Tots',
categoryId: 'fries',
categoryLabel: 'Fries',
tags: [ 'fries', 'potato', 'fried', 'tater' ]
}, {
label: 'Kombucha',
categoryId: 'drinks',
categoryLabel: 'Drinks',
tags: [ 'fermented', 'draft' ]
}, {
label: 'Hot Dog',
categoryId: 'dog',
categoryLabel: 'Hot Dogs',
tags: [ 'relish', 'onion', 'cheese sauce', 'chopped bacon' ]
}, {
label: 'Fries',
categoryId: 'fries',
categoryLabel: 'Fries',
tags: [ 'fries', 'cheese', 'potato', 'fried' ]
}, {
label: 'Tee Shirt',
categoryId: 'merchandise',
categoryLabel: 'Merchandise',
tags: [ 'apparel' ]
}
];
其余代码:
HTML:
<div class="block-selector">
<div class="search">
<input id="search" placeholder="Search Content Blocks" autocomplete="off" />
</div>
<div id="block-menu"></div>
</div>
CSS:
* {
box-sizing: border-box;
}
body, input {
font-size: 12px;
font-family: Helvetica, Arial, sans-serif;
}
.block-selector {
display: flex;
flex-direction: column;
margin: 40px;
/* height: 500px; */
width: 400px;
background: #fcfcfc;
/* border: 1px solid #E8E8E8; */
border: 1px solid red;
}
.block-selector .search {
position: relative;
border-bottom: 1px solid #E8E8E8;
}
.block-selector .search input {
padding: 11px;
outline: none;
border: none;
line-height: 28px;
width: 100%;
}
.block-selector .search input::placeholder {
color: silver;
}
.block-selector .icon {
width: 33px;
height: 33px;
}
#block-menu {
width: 100%;
height: 100%;
}
.categoryName {
background-color: gray;
width: 100%;
text-align: center;
border: 1px solid black;
}
.allItems {
display: flex;
flex-flow: wrap;
/* justify-content: flex-start; */
}
.block {
display: flex;
flex-direction: column;
flex: 1 0 0;
}
JS:
/*
https://docs.google.com/document/d/1fUBeQtBFC962aIztTb5TvvnetmRNL2J0IJ9ToSdmrvs/edit#heading=h.b98xc82r9k1v
*/
const blocks = [
{
label: 'Burger',
categoryId: 'burgers',
categoryLabel: 'Burgers',
tags: [ 'burger', 'lettuce', 'tomato' ]
}, {
label: 'Cheese Burger',
categoryId: 'burgers',
categoryLabel: 'Burgers',
tags: [ 'cheese', 'burger', 'lettuce', 'tomato' ]
}, {
label: 'Veggie Burger',
categoryId: 'burgers',
categoryLabel: 'Burgers',
tags: [ 'vegetable', 'burger', 'lettuce', 'tomato' ]
}, {
label: 'Fancy Burger',
categoryId: 'burgers',
categoryLabel: 'Burgers',
tags: [ 'cheese', 'burger', 'lettuce', 'tomato', 'fancy sauce' ]
}, {
label: 'Cheese Fries',
categoryId: 'fries',
categoryLabel: 'Fries',
tags: [ 'fries', 'cheese', 'potato', 'fried' ]
}, {
label: 'Shake',
categoryId: 'dessert',
categoryLabel: 'Dessert',
tags: [ 'shake', 'milk', 'chocolate', 'strawberry', 'frozen', 'dessert', 'sweet' ]
}, {
label: 'Tots',
categoryId: 'fries',
categoryLabel: 'Fries',
tags: [ 'fries', 'potato', 'fried', 'tater' ]
}, {
label: 'Kombucha',
categoryId: 'drinks',
categoryLabel: 'Drinks',
tags: [ 'fermented', 'draft' ]
}, {
label: 'Hot Dog',
categoryId: 'dog',
categoryLabel: 'Hot Dogs',
tags: [ 'relish', 'onion', 'cheese sauce', 'chopped bacon' ]
}, {
label: 'Fries',
categoryId: 'fries',
categoryLabel: 'Fries',
tags: [ 'fries', 'cheese', 'potato', 'fried' ]
}, {
label: 'Tee Shirt',
categoryId: 'merchandise',
categoryLabel: 'Merchandise',
tags: [ 'apparel' ]
}
];
const PLACEHOLDER_SRC = 'https://cdn3.iconfinder.com/data/icons/design-n-code/100/272127c4-8d19-4bd3-bd22-2b75ce94ccb4-512.png';
const blockMenuEl = document.getElementById('block-menu');
const renderBlock = (block) => {
const { label } = block;
const blockEl = document.createElement('div');
const iconEl = document.createElement('img');
const textEl = document.createElement('p');
blockEl.classList.add('block');
textEl.innerText = label;
iconEl.classList.add('icon');
iconEl.setAttribute('src', PLACEHOLDER_SRC);
blockEl.appendChild(iconEl);
blockEl.appendChild(textEl);
return blockEl;
};
blocks.forEach((block) => blockMenuEl.appendChild(renderBlock(block)));
/*
grouping:
When getting all the categories, create a block. Add style to that block
*/
const grouping = (blockMenuEl) => {
// get the categoryLabel
const results = groupItemByCategories(blocks);
// display them
// displayAccordingToCategories(results);
return results;
}
/*
get all the items according to 1 category
call the display function:
+) blockMenuEl.innerHTML = ''
+) take the category
+) create a new div for each item
+) blockMenuEl.appendChild(eachblock)
*/
const groupItemByCategories = (arr) => {
// get all the items name
/*
I could do the below by doing .map to get all the categoryLabel name
Then chain map and filter
*/
blockMenuEl.innerHTML = '';
const cateArr = arr.map(item => item.categoryLabel);
cateArr.forEach(cate => {
const filteredArr = arr.filter(arr => arr.categoryLabel === cate);
displayAccordingToCategories(filteredArr, cate);
})
}
const displayAccordingToCategories = (filteredArr, cate) => {
const categoryDiv = document.createElement('div');
categoryDiv.textContent = cate;
categoryDiv.classList.add('categoryName');
const allItems = document.createElement('div');
allItems.classList.add('allItems');
filteredArr.forEach(item => {
const itemDiv = renderBlock(item);
allItems.appendChild(itemDiv);
})
// categoryDiv.appendChild(allItems);
blockMenuEl.appendChild(categoryDiv);
blockMenuEl.appendChild(allItems);
}
grouping(blockMenuEl);
/*
search function
*/
const originalHTML = blockMenuEl.innerHTML;
const searchEle = (e) => {
// filter
const received = e.target.value;
const key = received.toLowerCase();
const filteredResults = filterEle(key);
// put res on the page
display(filteredResults);
}
const filterEle = (key) => {
const store = blocks.filter(block => {
const newLabel = block.label.toLowerCase();
const newCatLabel = block.categoryLabel.toLowerCase();
return newLabel.includes(key) || newCatLabel.includes(key) || block.tags.some(tag => tag.includes(key));
})
return store;
}
const display = (arr) => {
// this works but why
// can't do arr.length = 0 because filteredResults will never be 0
if(arr.length === blocks.length) {
blockMenuEl.innerHTML = originalHTML;
}else {
// why does this work
// because it clears the html so I can attach something on to it
blockMenuEl.innerHTML = '';
return arr.forEach(res => {
const result = renderBlock(res);
blockMenuEl.appendChild(result);
})
}
}
const search = document.querySelector('#search');
// figure out how to use arrow function here
search.addEventListener('keyup', searchEle);
解决方案
您正在使用 3 个循环:.map
, .forEach
,.filter
第一种近似(2个循环):
const groupItemByCategories = (arr) => {
blockMenuEl.innerHTML = '';
let categ = {};
for (let x of arr) {
if (!(x.categoryLabel in categ)) { categ[x.categoryLabel] = [] }
categ[x.categoryLabel].push(x)
}
for (let c in categ) {
displayAccordingToCategories(categ[c], c);
}
}
另请注意,对象访问比数组访问更快。
推荐阅读
- jquery - 如何使用 jQuery File Upload (Basic Plus UI 版)
- javascript - 静态 iframe 预览
- c++ - 逗号运算符如何与 C++ 中的 cout 一起使用?
- r - xy.coords(x, y) 中的错误:“x”和“y”长度不同的图表。PerformanceSummary R XTS
- javascript - npm 全局包:从包中引用内容文件
- python - python bokeh:在密码保护的仪表板中添加/更改子项时交互丢失。
- css - 样式化 jQuery UI 对话框标题?
- c - 通过将 char 转换为 USHORT 来获取错误的 UTF-8 值
- ruby-on-rails - Graphql没有在Rails中返回关联对象
- css - 防止 rgba 背景重叠