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

Javascript 如何优化下面的过滤器逻辑,使其仅使用1个循环而不是2个循环?,javascript,dictionary,filter,logic,Javascript,Dictionary,Filter,Logic,JSIDLE代码: 我目前有这段代码,认为解决方案相当麻烦,我想问一下,是否有其他方法可以使代码更高效地在1个循环中完成 在上面的代码中,我首先获取所有的categoryLabel,然后遍历下面的原始数组(块),并调用一个函数来显示页面上的所有项目 这是作为参数传入的数组: const blocks = [ { label: 'Burger', categoryId: 'burgers', categoryLabel: 'Burgers', tags: [ '

JSIDLE代码:

我目前有这段代码,认为解决方案相当麻烦,我想问一下,是否有其他方法可以使代码更高效地在1个循环中完成

在上面的代码中,我首先获取所有的
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:

JS:


您正在使用3个循环:
.map
.forEach
.filter

第一类近似(2个循环):

const groupItemByCategories=(arr)=>{
blockMenuEl.innerHTML='';
设categ={};
for(设arr的x){
如果(!(类别中的x.categoryLabel)){categ[x.categoryLabel]=[]}
类别[x.categoryLabel]。推送(x)
}
对于(c类){
按类别显示(类别[c],c);
}
}

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

您的解决方案不也使用3个循环吗?您是指关键字中的
?是的,它是通过一个循环实现的,但比在整个数组上使用过滤器检查它要短得多,速度也快得多:复杂性
O(n_类别)
vs
O(n_数组)
,访问对象与数组的计算速度也更快
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' ]
  }
];
<div class="block-selector">
  <div class="search">
    <input id="search" placeholder="Search Content Blocks" autocomplete="off" />
  </div>
  <div id="block-menu"></div>
</div>
* {
  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;
}
/*
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);