如何优化下面的过滤器逻辑,只使用1个循环而不是2个?



jsfield code: https://jsfiddle.net/bryanh210/j8yz2sep/3/

我目前有这个代码块,认为解决方案是相当麻烦的,想问是否有另一种方法,使代码更有效地在一个循环中完成。

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);
}
}

还要注意对象访问比数组访问快。

最新更新