На входе имеется строка типа:
processor_0_i3/chastota_0_1,5/ram_0_8/os_0_Windows/count_0_12/processor_1_i5/chastota_1_2,7/ram_1_16/os_1_Windows/count_1_10/processor_2_i3/chastota_2_1,5/ram_2_32/os_2_Linux/count_2_10
В ней записаны несколько опций одного товара.
Дальше строка перерабатывается в массив подобного типа:
Array
(
[processor] => Array
(
[0] => i3
[1] => i5
)
[chastota] => Array
(
[0] => 1,5
[1] => 2,7
)
[ram] => Array
(
[0] => 8
[1] => 16
[2] => 32
)
[os] => Array
(
[0] => Windows
[1] => Linux
)
[count] => Array
(
[0] => 12
[1] => 10
)
)
Из массива получаем селекты:
Теперь самое интересное: В полученных комбинациях селектов есть существующие комбинации опций, но есть и не существующие. На данном этапе, чтобы ограничить выбор неверных комбинаций, после каждого change
селекта, на сервер отправляется полученная комбинация, и сравнивается и существующими. Но это не совсем правильно, к тому же, неверные комбинации в селектах могут формироваться много раз подряд. В идеале, активность последних селектов должна формироваться в зависимости от первого выбранного. Допустим, при выбранном в первом селекте processor i3
, в последующих активными должны быть только те опции, которые состоят в комбинациях с первой выбранной опцией. Как это реализовать?
Для выполнения такой задачи, нужно всё таки изначально с сервера передавать массив всех версий продукта, а не только уникальных значений для полей. Что-то похожее на это:
<?php
// Исходная строка с характеристиками одного товара.
$product = 'processor_0_i3/chastota_0_1,5/ram_0_8/os_0_Windows/count_0_12/processor_1_i5/chastota_1_2,7/ram_1_16/os_1_Windows/count_1_10/processor_2_i3/chastota_2_1,5/ram_2_32/os_2_Linux/count_2_10';
// Сюда будем записывать версии товара.
$versions = [];
// Также сложим общее количество версий.
$count = 0;
// Создадим предварительно массив, разбив строку. Разделитель - `/`.
$array = explode('/', $product);
foreach ($array as $params) {
// В данном примере проигнориваны проверки.
// Опять разбиваем строку, чтобы выделить каждую характеристику.
// Разделитель - `_`, количество - три.
[$property, $index, $value] = explode('_', $params, 3);
// Записываем версию товара.
$versions[$index][$property] = $value;
// Если характеристика == `count`,
// то складываем в общее количество.
if ('count' === $property) $count += (int) $value;
}
// Debug.
// Характеристики: процессоры, частоты, планки, оси. Уникальные значения.
// $processors = array_keys(array_column($versions, 'processor', 'processor'));
// $frequencies = array_keys(array_column($versions, 'chastota', 'chastota'));
// $memories = array_keys(array_column($versions, 'ram', 'ram'));
// $oses = array_keys(array_column($versions, 'os', 'os'));
// print_r(compact('versions', 'count', 'processors', 'frequencies', 'memories', 'oses'));
И основываясь на этот массив версий, и прибегая к функциям по работе с массивами map, filter, every, reduce динамически перестраивать выпадающие списки. Как один из вариантов:
// Данные для работы с `javascript` должны быть в `json` формате.
// const VERSIONS = <?= json_encode($versions) ?>;
// Массив версий товара.
const VERSIONS = [{
"processor": "i3",
"chastota": "1,5",
"ram": "8",
"os": "Windows",
"count": "12"
}, {
"processor": "i5",
"chastota": "2,7",
"ram": "16",
"os": "Windows",
"count": "10"
}, {
"processor": "i3",
"chastota": "1,5",
"ram": "32",
"os": "Linux",
"count": "10"
}]
// `NodeList` из выпадающих списков.
const SELECTORS = document.querySelectorAll('select.linked__data')
// Чтобы не нагружать постоянными пересчетами.
const ARRAY_FROM_SELECTORS = Array.from(SELECTORS)
// Поле вывода количества отфильтрованных версий товара.
const SPAN_COUNT = document.getElementById('count')
// Элемент для вывода отфильтрованных версий товара.
const LIST_RESULT = document.getElementById('result')
// Формирование запроса для выбранных значений всех `select`.
// Пустые значения игнорируются.
function selectedQuery(currentIndex) {
let search = {}
ARRAY_FROM_SELECTORS.map(function(select, index) {
// Сбрасываем `value` если index больше текущего.
if (index > currentIndex) select.value = ''
// Если в списке выбран пункт, добавляем в запрос.
if (!!select.value) search[select.name] = select.value
})
return search
}
// Массив отфильтрованных объектов, основанных на
// индексе текущего выбранного выпадающего списка
// и сформированного запроса.
function getFiltered(currentIndex) {
let query = selectedQuery(currentIndex)
let queryKeys = Object.keys(query)
return VERSIONS
.filter(version => queryKeys
.every(prop => version[prop] == query[prop])
)
}
// Построение таблицы.
function renderTable(filtered) {
// Debug.
// console.clear()
// console.table(filtered)
// Очистим форму вывода.
LIST_RESULT.innerHTML = ''
// Пройдемся по отфильтрованным версиям.
filtered.map(function(item) {
let li = document.createElement('li')
li.append(document.createTextNode(JSON.stringify(item)))
LIST_RESULT.appendChild(li)
})
}
// Отрисовка доступного количества.
function renderCount(filtered) {
SPAN_COUNT.innerText = filtered
.map(item => item.count)
.reduce((total, current) => parseFloat(total) + parseFloat(current))
}
// Перестроение выпадающих списков.
function renderSelecotrs(filtered, currentIndex = 0) {
// По отфильтрованным данным формируем доступные выпадающие списки.
// Для текущего списка ничего не меняем, поэтому currentIndex + 1,
// то есть начинаем формирование только со следующего.
for (let i = currentIndex + 1; i < SELECTORS.length; i++) {
let name = SELECTORS[i].name
// Получим список всех возможных уникальных значений для текущего списка.
let unique = [...new Set(filtered.map(item => item[name]))]
Array.from(SELECTORS[i])
.map(option => {
// Предварительно скроем все элементы в списке.
option.hidden = 'hidden'
// Если элемент списка не имеет `value`
// или `value` находится в списке уникальных.
// то покажем его.
if ('' == option.value || unique.includes(option.value)) {
option.hidden = ''
}
})
}
}
// Функция фильтрации.
function filterList(event = null, currentIndex = 0) {
// Получаем отфильтрованные данные.
let filtered = getFiltered(currentIndex)
// Построим список отфильтрованных версий.
renderTable(filtered)
// Обновим количество.
renderCount(filtered)
// Обновим выпадающие списки.
renderSelecotrs(filtered, currentIndex)
}
// Регистрируем обработчика события для каждого выпадающего списка.
ARRAY_FROM_SELECTORS
.map(function(item, index) {
item.addEventListener('input', (event) => filterList(event, index))
})
// Инициируем запуск фильтрации.
filterList()
form {
display: flex;
}
.form-group {
width: 25%;
margin: 15px 0;
}
.form-control {
display: block;
width: 100%;
padding: .375rem .75rem;
border: 1px solid #ced4da;
box-sizing: border-box;
}
<form action="">
<div class="form-group">
<label>processor</label>
<select name="processor" class="form-control linked__data">
<option value="i3" selected>i3</option>
<option value="i5">i5</option>
</select>
</div>
<div class="form-group">
<label>chastota</label>
<select name="chastota" class="form-control linked__data">
<option value="" selected>Выберите</option>
<option value="1,5">1,5</option>
<option value="2,7">2,7</option>
</select>
</div>
<div class="form-group">
<label>ram</label>
<select name="ram" class="form-control linked__data">
<option value="" selected>Выберите</option>
<option value="8">8</option>
<option value="16">16</option>
<option value="32">32</option>
</select>
</div>
<div class="form-group">
<label>os</label>
<select name="os" class="form-control linked__data">
<option value="" selected>Выберите</option>
<option value="Windows">Windows</option>
<option value="Linux">Linux</option>
</select>
</div>
</form>
<h1 id="count"></h1>
<ul id="result"></ul>
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
как правильно написать что при каждом цикле получить последовательную цифру типа
Есть входные данные типа логин, пароль и тд