Ответы пользователя по тегу JavaScript
  • Как выдернуть массив чисел из строки?

    0xD34F
    @0xD34F Куратор тега JavaScript
    (str.match(/[0-9]+/g) ?? []).map(Number)
    
    // или
    
    Array.from(str.matchAll(/\d+/g), n => +n)
    
    // или
    
    str.split(/\D+/).filter(Boolean).map(parseFloat)
    
    // или
    
    eval(`[${str.replace(/\D+/g, (m, i) => i ? ',' : '')}]`)
    
    // или
    
    [...str].reduce((acc, n, i, a) => (
      isNaN(n) || (isNaN(a[i - 1]) && acc.push(0), acc.push(acc.pop() * 10 + n * 1)),
      acc
    ), [])
    Ответ написан
    Комментировать
  • Преобразование объекта в примитив, почему прилетает object Object?

    0xD34F
    @0xD34F Куратор тега JavaScript
    ищет метод toString - его нет

    То есть, если попробовать получить test.toString, то результатом будет undefined. Вы проверьте, так ли это. Будете удивлены.
    Ответ написан
  • Как получить значение ячейки th и присвоить это значение в data-attr td?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Какие таблицы будем обрабатывать:

    const tables = document.querySelectorAll('селектор таблиц');

    Собираем в массив заголовки столбцов, используем элементы этого массива для задания значений атрибута. Как определять, какое именно значение брать для конкретной ячейки? - очевидно же, у них одинаковые индексы:

    for (const { tHead, tBodies } of tables) {
      const labels = Array.prototype.map.call(
        tHead.rows[0].cells,
        th => th.textContent
      );
    
      for (const { rows } of tBodies) {
        for (const { cells } of rows) {
          for (const [ i, label ] of labels.entries()) {
            cells[i].dataset.label = label;
          }
        }
      }
    }

    или

    tables.forEach(table => {
      table.querySelectorAll('tbody td').forEach(function(td) {
        td.setAttribute('data-label', this[td.cellIndex]);
      }, Array.from(table.querySelectorAll('thead th'), th => th.innerText));
    });
    Ответ написан
    Комментировать
  • Как собрать массив из двух других массивов по совпадению id?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const result = data
      .flatMap(n => n.values)
      .filter(n => n.Ids.some(m => arr.includes(m)))
      .map(({ id, title }) => ({ id, title }));

    или

    const inArr = Set.prototype.has.bind(new Set(arr));
    const data.reduce((acc, { values }) => (
      values.forEach(({ Ids, ...n }) => Ids.some(inArr) && acc.push(n)),
      acc
    ), []);
    Ответ написан
    1 комментарий
  • Как из двух массивов получить те объекты, которые есть в одном и отсутствуют в другом?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Делаем просто, ровно то, что спрошено в вопросе:

    const result = allCass.filter(function(n) {
      return !this.has(n.id);
    }, new Set(defaultCass.map(n => n.id)));

    Делаем сложно, решаем задачу в более общем виде (источником данных могут быть не обязательно массивы; условие, по которому значения признаются равными, не будем зашивать непосредственно в код - будет параметром функции):

    function* diff(data1, data2, key = n => n) {
      const getKey = key instanceof Function ? key : n => n[key];
      const keys = new Set;
    
      for (const n of data2) {
        keys.add(getKey(n));
      }
    
      for (const n of data1) {
        if (!keys.has(getKey(n))) {
          yield n;
        }
      }
    }

    В вашем случае применять так:

    const result = [...diff(allCass, defaultCass, 'id')];

    А можно и так:

    Array.from(diff('abcdE', 'AcD', n => n.toLowerCase())) // ['b', 'E']

    Или так:

    for (const n of diff(Array(8).keys(), Array(5).keys())) {
      console.log(n); // 5 6 7
    }
    Ответ написан
    6 комментариев
  • Как сформировать новый массив объектов, сделав слияние по некоторому параметру?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Пытался сделать через reduce из библиотеки lodash

    Давайте так - или lodash, или reduce, но не оба сразу:

    const newArr = _.map(_.groupBy(arr, 'merch'), (v, k) => ({
      merch: +k,
      games: _.uniq(_.flatMap(v, 'games')),
    }));

    const newArr = Object
      .entries(arr.reduce((acc, n) => ((acc[n.merch] ??= []).push(...n.games), acc), {}))
      .map(n => ({ merch: +n[0], games: [...new Set(n[1])] }));
    Ответ написан
    Комментировать
  • Как сделать сортировку по двум параметрам?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Сортируем существующий массив:

    arr.sort((a, b) => a.surname.localeCompare(b.surname) || a.name.localeCompare(b.name));

    Собираем отсортированный новый:

    const sorted = (arr, keys) => arr
      .map(n => [ n ].concat(keys(n)))
      .sort((a, b) => {
        let diff = 0;
        a.find((n, i) => diff = i && ((n < b[i]) ? -1 : +(n > b[i])));
        return diff;
      })
      .map(n => n[0]);
    
    
    const sortedArr = sorted(arr, n => [ n.surname.toLowerCase(), n.name.toLowerCase() ]);
    Ответ написан
    Комментировать
  • Почему заполняется только один select?

    0xD34F
    @0xD34F Куратор тега JavaScript
    selects[0].appendChild(option)
    selects[1].appendChild(option)

    Как один элемент может одновременно находиться внутри двух других элементов? Очевидно же, что никак:

    Если данный дочерний элемент является ссылкой на существующий узел в документе, то функция appendChild() перемещает его из текущей позиции в новую позицию

    Это должны быть два разных option'а. Можете во втором случае добавлять копию:

    selects[1].appendChild(option.cloneNode(true));

    Но вообще, вариант так себе. Появится ещё один select - побежите копипастить строку с добавлением option'а? Надо сделать цикл по select'ам. Например, так:

    selects.forEach(function(n) {
      n.append(...this.map(m => new Option(m[1].label, m[0])));
    }, Object.entries(metrics.convertRules));

    Или, можно собирать html вместо создания отдельных элементов:

    const optionsHTML = Object
      .entries(metrics.convertRules)
      .map(n => `<option value="${n[0]}">${n[1].label}</option>`)
      .join('');
    
    selects.forEach(n => n.innerHTML = optionsHTML);
    Ответ написан
  • Как заполнить произведение для каждой ячейки?

    0xD34F
    @0xD34F Куратор тега JavaScript
    62b0f00f35c0f358713719.png

    "Добавить"? Понятно, почему ничего не получается - формы добавляются динамически, так что обработчики событий вы пытаетесь назначать элементам, которых ещё нет. Применим делегирование - обработчик добавляем один раз, выше форм, проверяем целевой элемент, если это оказался интересующий нас input, то поднимаемся от него к элементу формы, ищем внутри неё все input'ы и элемент, в который надо записать результат вычислений.

    document.addEventListener('input', e => {
      if (e.target.classList.contains('form-control')) {
        const form = e.target.closest('селектор формы');
        form.querySelector('span').innerText = Array
          .from(form.querySelectorAll('input.form-control'))
          .reduce((acc, n) => acc * (+n.value || 0), 1);
      }
    });
    Ответ написан
    Комментировать
  • Как правильно распарсить такой массив со строками?

    0xD34F
    @0xD34F Куратор тега JavaScript
    arr.reduce((acc, n) => (
      n = n.match(/(\S+) = (.*)/),
      n && (acc[n[1]] = n[2]),
      acc
    ), {})
    Ответ написан
    2 комментария
  • Как с помощью JQUERY перебрать и сложить значение нескольких select?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Как будем складывать значения элементов:

    const sum = elements =>
      Array.prototype.reduce.call(
        elements,
        (acc, n) => acc + (+n.value || 0),
        0
      );

    Сами элементы могут быть представлены как jquery-объект, NodeList или HTMLCollection (ну и разумеется, обычный массив тоже будет обсчитан как надо):

    const $form = $('form').on('change', 'select', () => {
      $('input').val(sum($form.find('select')));
    });

    const input = document.querySelector('input');
    const selects = document.querySelectorAll('form select');
    const onChange = () => input.value = sum(selects);
    selects.forEach(n => n.addEventListener('change', onChange));

    document.querySelector('form').addEventListener('change', function(e) {
      if (e.target.tagName === 'SELECT') {
        document.querySelector('input').value = sum(this.getElementsByTagName('select'));
      }
    });
    Ответ написан
    1 комментарий
  • Как убрать checked с input radio при клике на него?

    0xD34F
    @0xD34F Куратор тега JavaScript
    А что за задача-то решается? Нужен гибрид радиокнопки и чекбокса?

    Вариант раз, радиокнопки:

    let checked = null;
    
    $('input').click(function() {
      checked = checked === this.value ? null : this.value;
      this.checked = !!checked;
    });
    
    // или
    
    document.querySelectorAll('input').forEach(function(n) {
      n.addEventListener('click', this);
    }, function({ target: t }) {
      t.checked = !!(this[0] = this[0] === t ? null : t);
    }.bind([]));

    Вариант два, с заменой радиокнопок чекбоксами:

    const $checkboxes = $('input').change(function() {
      if (this.checked) {
        $checkboxes.not(this).prop('checked', false);
      }
    });
    
    // или
    
    const checkboxes = document.querySelectorAll('input');
    const onChange = e => checkboxes.forEach(n => n.checked &&= n === e.target);
    checkboxes.forEach(n => n.addEventListener('change', onChange));

    Вариант три - вместо того, чтобы изменять значение свойства checked вручную, добавьте ещё одну радиокнопку, которая будет обозначать отсутствие выбора.
    Ответ написан
    1 комментарий
  • Как передать в аргументы функции карирования старые аргумент + новые?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const sum5 = (...args) =>
      args.length > 4
        ? args.slice(0, 5).reduce((acc, n) => acc + n, 0)
        : sum5.bind(null, ...args);
    //  или
    //  : (...args2) => sum5(...args, ...args2);
    Ответ написан
    1 комментарий
  • Как сделать, чтобы при выборе значения в одном select'е оно пропадало в других?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const selects = [...document.querySelectorAll('select')];
    const onChange = () =>
      selects.forEach(function({ value, options: [...n] }) {
        n.forEach(m => m.hidden = this(m.value) && value !== m.value);
      }, Set.prototype.has.bind(new Set(selects.map(n => n.value))));
    
    selects.forEach(n => n.addEventListener('change', onChange));
    Ответ написан
    1 комментарий
  • Как "развернуть" таблицу, превратив строки в столбцы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function transposeTable(table) {
      const headerCol = table.rows[0]?.cells[1]?.tagName === 'TH';
      const content = Array.from(
        table.rows,
        tr => Array.from(tr.cells, td => td.innerHTML)
      );
    
      table.innerHTML = content[0]?.map((n, i) => `
        <tr>${content.map((m, j) => (j = (headerCol ? j : i) ? 'td' : 'th', `
          <${j}>${m[i]}</${j}>`)).join('')}
        </tr>
      `).join('') ?? '';
    }

    или

    function transposeTable(table) {
      const cells = Array
        .from(table.rows, tr => [...tr.cells])
        .reduce((acc, n) => (
          n.forEach((m, j) => (acc[j] ??= []).push(m)),
          acc
        ), []);
    
      Array.prototype.forEach.call(table.children, n => n.remove());
      cells.forEach(n => table.insertRow().append(...n));
    }

    https://jsfiddle.net/mqjna81x/
    Ответ написан
    Комментировать
  • Как проверить, что набор карт образует покерную комбинацию "стрит"?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Сделать вспомогательный объект, где для нечисловых карт будут указываться их числовые эквиваленты. Массив карт превратить в массив соответствующих им чисел (карты используется как ключ для извлечения значения из объекта, если ничего не найдено, то карта приводится к числу); отсортировать массив; убедиться, что разность соседних значений всегда составляет единицу.

    const weights = {
      j: 11,
      q: 12,
      k: 13,
      a: 14,
    };
    
    const isStraight = hand => hand
      .map(n => weights[n] ?? +n)
      .sort((a, b) => a - b)
      .every((n, i, a) => !i || (n - a[i - 1] === 1));
    Ответ написан
    Комментировать
  • Как убрать disabled при повторном нажатии на чекбокс?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Не надо отслеживать никаких "повторных" "нажатий".
    Достаточно поставить состояние кнопки в зависимость от состояния чекбокса:

    чекбокс.addEventListener('change', e => кнопка.disabled = !e.target.checked);
    Ответ написан
    1 комментарий
  • Как отобразить блок "Нет элементов"?

    0xD34F
    @0xD34F Куратор тега JavaScript
    $('.elem_block').on('click', '.dell', function() {
      $(this).closest('.block').addClass('fx_none');
      $('.no-res').toggleClass('fx_none', !!$('.block:not(.fx_none)').length);
    });

    или

    document.addEventListener('click', e => {
      const btn = e.target.closest('.dell');
      if (btn) {
        btn.closest('.block').classList.add('fx_none');
        document.querySelector('.no-res').classList.toggle(
          'fx_none',
          !!document.querySelector('.block:not(.fx_none)')
        );
      }
    });

    UPD. Наконец-то дождались. Теперь можно сделать так:

    • Класс fx_none вырезаем
    • Элементам .no-res и .elem_block добавляем общую обёртку, какой-нибудь <div class="container">
    • По кликам на .dell вместо добавления класса, скрывающего элементы, удаляем их по-настоящему:

      document.querySelectorAll('.dell').forEach(function(n) {
        n.addEventListener('click', this);
      }, e => e.target.closest('.block').remove());
    • Прячем .no-res, если в .elem_block что-то есть:

      .container:has(.elem_block *) .no-res {
        display: none;
      }

      Если же удаление элементов не вариант, то прятать .no-res надо в том случае, если существует .block без класса, которые его скрывает:

      .container:has(.block:not(.fx_none)) .no-res {
        display: none;
      }


    Ответ написан
    Комментировать
  • Как переделать код для множества segmented control?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const controls = document.querySelectorAll('.segmented-control');
    controls.forEach(n => n.addEventListener('change', updatePillPosition));
    window.addEventListener('resize', () => controls.forEach(n => updatePillPosition.call(n)));
    
    function updatePillPosition() {
      const inputs = [...this.querySelectorAll('.option input')];
      const x = this.offsetWidth / inputs.length * inputs.findIndex(n => n.checked);
      this.querySelector('.selection').style.transform = `translateX(${x}px)`;
    }
    Ответ написан
    Комментировать