Обичайно потребителско изживяване е да филтрирате резултатите до насита – просто помислете за последния път, когато сте огладнели и сте търсили ресторанти със среден рейтинг › 4 звезди в радиус от няколко мили.

Когато работихме по първия ни проект за Flatiron School, моят екип искаше да предложи на нашите потребители същата свобода. Изтеглихме данни от API на услугата за национални паркове, мислейки, че би било страхотно да създадем приложение от една страница, където можете да филтрирате паркове по дейност или състояние.

Като нов програмист обаче може да е трудно да вземете външни данни и да ги манипулирате по начина, по който ви е необходимо. Много по-трудно, отколкото да се убедите, че да, наистина трябва да планирате пътуване до Национален парк Акадия.

Ето няколко подхода за филтриране на масиви в JavaScript, които могат да помогнат в подобна ситуация, по-специално чрез намиране на пресечната точка на два масива. Можете да мислите за пресичанекатоколекция от елементи, които присъстват и в двата масива.

Масивни методи

Нека започнем с преглед на няколко полезни метода, с които разполагаме. Ще ги използваме в нашите примери по-долу.

включва( )

  • Взема един параметър, нашият „термин за търсене“
  • Връща true или false в зависимост от това дали параметърът съществува или не в масива

филтър ( )

  • Взема един параметър, функция за обратно извикване. Тук можем да създадем всяка функция, която оценява на true за елементите, които искаме да запазим в резултатите, и false в противен случай. includes() по-горе отговаря на тези критерии - това ще бъде полезно по-късно.
  • Връща копие на масива с резултатите

Това са бързи определения. Можете да намерите пълна справка за всеки метод в MDN тук и тук.

Сравняване на плоски масиви

Ако нашите масиви нямат вложени елементи, filter() и includes() могат да ни помогнат да върнем общи елементи само с няколко реда код.

За да запазим нашата тема за паркове, нека кажем, че имаме два масива – един за налични летни дейности и един за зимни.

const summer = ['hiking', 'swimming', 'guided tours', 'museum exhibits']

const winter = ['skiing', 'snowboarding', 'guided tours', 'museum exhibits']

Ако се интересуваме само от целогодишни дейности, искаме масив само с низовете от зимата и лятото. Можем да използваме filter() в нашия летен масив, след което да създадем функция за обратно извикване, която взема едно activity наведнъж и проверява за присъствието му в нашия зимен масив.

const yearRound = summer.filter(activity => winter.includes(activity))

// => [ 'guided tours', 'museum exhibits' ]

Може да мислите за подхода тук като:

  • summer.filter Започнете с летния масив
  • activity => Вижте всяка дейност (низ)
  • winter.includes(activity) Определете дали зимният масив има съответстваща дейност (низ). Ако да, върнете полученото копие на летния масив.

Сравняване на вложени масиви

Вероятно масивите, които бихме искали да сравним, няма да бъдат прости низове, особено ако работим с външен API. В този пример ще разгледаме как можем да проверим дали даден вложен масив съдържа елементи от друг.

Да кажем, че сега имаме набор от обекти, които да представят нашите паркове. Всеки парк има име, състояние и налични дейности. Последният има свой собствен масив като стойност.

Отделно имаме масив, който ще наречем userInput — независимо от дейностите, които нашите потребители са избрали като интересни за тях и искат да взаимодействат с тях в нашето приложение.

const parks = [ 
  {
    name: 'Acadia National Park', 
    state: 'Maine',
    activities: ['boating', 'camping', 'fishing', 'skiing']
  }, 
  {
    name: 'Olympic National Park', 
    state: 'Washington',
    activities: ['hiking', 'snowshoeing', 'tubing', 'kayaking']
  }, 
  {
    name: 'Arches National Park', 
    state: 'Utah',
    activities: ['canyoneering', 'climbing', 'stargazing', 'horse trekking']
  }
] 

const userInputs = ['hiking', 'stargazing']

Не забравяйте, че includes() приема един елемент за търсене. Ако се опитаме да съпоставим нашето точно решение по-горе, с нещо като parks.filter(park => userInputs.includes(park.activities)), няма да получим никакви резултати.

И така, как да получим достъп до този вложен масив? Ще трябва да разширим нашата функция за обратно извикване, за да преминем от обекта parks надолу към parks.activities

const filteredParks = parks.filter(park => {
 *// add filter to the parks array and pass each park to the callback function

  for (const activity of park.activities) {
*// go through each activity for this park 

    if (userInputs.includes(activity)){
*// if our user inputs array also has the same activity string

      return true 
*// this park meets our condition
*// include the park in the array copy returned by .filter()

    } 
  }
})

/* => [
  {
    name: 'Olympic National Park',
    state: 'Washington',
    activities: [ 'hiking', 'snowshoeing', 'tubing', 'kayaking' ]
  },
  {
    name: 'Arches National Park',
    state: 'Utah',
    activities: [ 'canyoneering', 'climbing', 'stargazing', 'horse trekking' ]
  }
]
*/

Тук нашият for цикъл ни приближава до информацията, която всъщност ни интересува, дейностите, докато не можем да проверим дали интересът на потребителя включва дейности, свързани с определен парк.

Тъй като функцията за обратно извикване за filter() трябва да върне вярна или невярна стойност, ние ръчно връщаме true, ако интересите на нашия потребител включват нашия низ за активност. В нашите резултати завършваме с мач от Олимпийски национален парк на 'hiking' и мач от Национален парк Арки на 'stargazing'.

Ако не работите с много данни, това може да свърши работа. Важно е обаче да се отбележи, че в момента преминаваме през всякаизбрана от потребителя дейност за всекиелемент от всекимасив от дейности в нашите паркове. Това ще стане по-неефективно, ако дължината на нашите масиви се увеличи.

Един от начините да смекчите това е да използвате „набор“. На най-просто ниво множествата са колекция от стойности. Те имат някои важни разлики от масивите и за по-задълбочено въведение бих препоръчал това видео в „Пълното ръководство за JS набори“.

Това, което трябва да знаем днес е, че можем да проверим дали даден елемент съществува в набор по-бързо, отколкото в масив. Без значение колко дълъг става нашият масив за park.activities, ако създадем набор от стойностите, можем да намерим нашия activity, който ни интересува, за същия период от време.

const filterParks = parks.filter(park => {
*// add filter to the parks array and pass each park to the callback function

  const parkActivities = new Set(park.activities)
*// create a set with all the activities for this specific park 

  for (const activity of userInputs) {
*// for each activity the user selected 

      if (parkActivities.has(activity)) {
*// if the set has that same activity string

        return true
*// this park meets our condition
*// include the park in the array copy returned by .filter()
      }
  }
})

/* => [
  {
    name: 'Olympic National Park',
    state: 'Washington',
    activities: [ 'hiking', 'snowshoeing', 'tubing', 'kayaking' ]
  },
  {
    name: 'Arches National Park',
    state: 'Utah',
    activities: [ 'canyoneering', 'climbing', 'stargazing', 'horse trekking' ]
  }
]
*/

Тук използваме метода has() за нашия набор, а не includes() в нашия масив. Методът също така взема параметър на елемент за търсене, който да търси в набора.

При този актуализиран подход все още трябва да обикаляме всеки парк, но не и всеки масив от вложени дейности. За повече информация относно пресичанията и комплектите вижте този пример от Geeks for Geeks.

И ето го, филтрирахме нашия масив въз основа на съдържанието на друг. Надявам се това да ви помогне да започнете да работите с филтри във вашите собствени приложения.