Подготавливаем проект

Продолжим работу с данными. Сегодня у нас будет функция filter

Суть функции filter заключается в том чтобы взять и отфильтровать данные, то есть взять список и оставить в нем подходящие данные, убрать не подходящие.

Чтобы не ломать код, которые был сделан в прошлом задании, я создам новую функцию в main.js и назову ее processFilter, запихаю в нее стягивание списка данных, вот так:

чтобы не захламлять console.log, убери все вызовы console.log из функции process

То есть при запуске приложения должно быть так

Пробуем фильтровать

Как и функции forEach и map, функция filter работает по такому же принципе. Передаем ей анонимную функцию и чего-то происходит.

Вспоминаем

У forEach в анонимной функции может быть что угодно, как правило это какая-та операция. И функция не должна ничего возвращать (хотя в принципе может, но это ни на что влиять не будет)

Например:

data.forEach(item => {
    console.log(item);
})

У map анонимная функция обязательно должна вернуть какое-то новое значение, а результат выполнения функции обязательно кладут в переменную. Например:

let newData = data.forEach(item => {
    return item['ключик'];
})

Новый список который создает функция map всегда содержит столько же значений сколько и исходный список, можно вспомнить такую картинку

тут в консоли два раза выводятся списки длины 38. В первом случае – исходный, а во втором – список значений по ключу на каждый элемент из исходного списка.

Отличие функции filter

Так вот, функция filter, берет исходный список и оставляет в нем только элементы, удовлетворяющие некоторому условию.

У filter анонимная функция должна вернуть логическое значение (ложь либо истину).

Если значения анонимной функции получается истинное, то элемент попадает в новый список, а если ложное – то не попадает.

Давай чтобы было понятнее, сначала рассмотрим на простом примере. Ну типа вот таком

let filteredData = [1,2,3,4].filter(item => {
    return item > 2
})
console.log(filteredData);

что же вернет эта вундервафля:

как видишь, он возвращает элементы, которые большие двух, что собственно в return item > 2 и написано.

Мы можем сделать хитрое условие какое-нибудь

let filteredData = [1,2,3,4].filter(item => {
    return item > 1 && item < 4
})

и тут работает. Потрясающе! =О

Кстати смотрим, так как тут у нас анонимная функция, которая просто возвращает значение, это значит, что этот код можно писать короче, вот так:

let filteredData = [1,2,3,4].filter(item => item > 1 && item < 4)

жутковато-то получилось, но в жизни так часто пишут =)

Фильтруем данные по ключу

И так вернемся к нашему списку:

[
    ...
    {
        "ID": "1385327090",
        "Отметь, в какой мере ты удовлетворен курсом?": "В основном",
        "Насколько курс был интересен?": "Интересный",
        "Насколько курс был полезен?": "Полезный",
        "Насколько материал курса был понятен?": "В основном",
        "Насколько доволен форматом обучения?": "В основном",
        "Оцени работу преподавателя по шкале от 1-10 / 10 – высоко оцениваю работу преподавателя. 1 – совсем не понравилась работа преподавателя": "8"
    },
    ...
] 

я хочу запросить список отзывов, в которых отметили для ключа "Насколько курс был полезен?" значение "Полезный"

Как же это сделать? А нет ничего проще, просто пишем:

let filteredData = data.filter(item => item["Насколько курс был полезен?"] == "Полезный")
console.log(filteredData);

проверяем как работает условие

красота! =)

Попробуй поиграться с другими ключами =О

А если чувствуешь себя уверено, то идем дальше

Выводим результат на страницу

Давай теперь попробуем выводить список на страницу.

Создадим функцию и назовем ее fillList, загоним в нее стандартный код скачивания списка

async function fillList() {
    let r = await fetch("/data.json");
    let data = await r.json();
    console.log(data);
}

в сумме должно так выглядеть

теперь в файлике index.html, создадим таблицу под элементы списка. Я буду использовать табличку https://getbootstrap.com/docs/5.3/components/table/

<div class="container mt-2">
  <table class="table" id="elements-container">
    <thead>
      <tr>
        <th>ID</th>
        <th>Полезность</th>
        <th>Довольность</th>
        <th>Удовлетворенность</th>
      </tr>
    </thead>
    <tbody></tbody>
  </table>
</div>

вот так получится

идем обратно в main.js

так как я буду добавлять строки в тело таблицы, мне надо по id таблицы вытащить это самое tbody. Для этого надо написать вот так:

let container = document.querySelector("#elements-container > tbody");
console.log(container)

А теперь надо его заполнить. Заполняем, как и выпадающий список в прошлом задании через forEach.

Мы воспользуемся классным методом insertAdjacentHTML, который позволяет передать текст разметки, а он его возьмет и автоматом вставит в контейнер. Получается такое:

data.forEach(item => {
    container.insertAdjacentHTML("beforeend", `
    <tr>
        <td>${item['ID']}</td>
        <td>${item['Насколько курс был полезен?']}</td>
        <td>${item['Насколько доволен форматом обучения?']}</td>
        <td>${item['Отметь, в какой мере ты удовлетворен курсом?']}</td>
    </tr>
    `);
})

попробуем теперь пофильтровать выводимые данные.

Фильтруем список

У нас сейчас выводится весь список, но было бы прикольно если бы мы могли вверху выбрать в выпадающем списке значение, и он бы оставил только те строки, которые соответствуют выбранному вверху значению.

Например, оставить только те строки полезность которых была “Полезной”

Попробуем сначала просто вывести отфильтрованный список. Сделаем это прямо в методе fillList

проверяем

а что если я хочу брать значение с формы. Тогда надо написать так:

проверим как работает:

чего-то не работает… Почему так?

Перехватываем событие выбора значения

Дело в том, что наша функция fillList срабатывает ровно один раз при запуске приложения. А вот когда мы переключаем значение в списке, то ничего не происходит.

Мы можем добавить обработчик события изменения значения в списке. Для этого добавим функцию onSelectPoleznostChanged

теперь пойдем в index.html и привяжем эту функцию в качестве обработчика события смены значения в списке. Делается это так:

давай теперь добавим какой-нибудь вывод в консоль в функцию onSelectPoleznostChanged

async function onSelectPoleznostChanged() {
    console.log("Поменялся")
}

проверим как работает:

а теперь попробуем взять значение из списка

async function onSelectPoleznostChanged() {
    let selectPoleznost = document.querySelector("#selectPoleznost")
    console.log(selectPoleznost.value)
}

тестируем:

красота! =)

А попробуем-ка вызывать метод fillList() внутри этой функции,

async function onSelectPoleznostChanged() {
    let selectPoleznost = document.querySelector("#selectPoleznost")
    console.log(selectPoleznost.value)
    fillList()
}

проверяем:

ну что-то происходит…

Если присмотреться, то наш список который выводится на странице – растет.

из-за чего это происходит?

Дело в том, что мы в методе fillList всегда добавляем элементы в наш список, и никогда не удаляем

давай добавим перед вызовом forEach() строчку, где будем чистить список:

проверим что получилось:

отлично, все работает! =)

Добавляем возможность не фильтровать

Возможность отфильтровать – это конечно здорово. Но что, если я хочу возможность сбросить фильтр?

Для выпадающего списка это обычно решается путем добавления пункта типа “не важно” или “все”

Идем в index.html и добавляем там:

мы добавили один пункт меню вручную, остальные у нас добавляются, как ты помнишь, динамически

единственная проблема сейчас, этот фильтр не работает:

в принципе это логично, ведь мы фильтруем вот так

let filteredData = data.filter(item => item['Насколько курс был полезен?'] == selectPoleznost.value)

когда мы выбрали значение не важно, он начинает оставлять те записи у которых в отзыве по ключу item['Насколько курс был полезен?'] стоит значение не важно. А таких нет…

Значит надо просто значения не важно обрабатывать отдельно. С точки зрения логического выражения это можно записать так:

let filteredData = data.filter(item => {
    // чтобы не получалось сильно длинных строчек, я перепишу анонимную функцию через return
    return selectPoleznost.value == 'не важно' || item['Насколько курс был полезен?'] == selectPoleznost.value
})

супер =)

вот такой итоговый код получается

Теперь можно и задание поделать =О

Задание

Реализовать возможность фильтровать по оставшимся выпадающим спискам.

Причем фильтры по спискам должны объединятся.

Как-то так должно работать: