Задать вопрос
Ответы пользователя по тегу JavaScript
  • Преобразование объекта в примитив, почему прилетает 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));
    });
    Ответ написан
    Комментировать
  • Как из свойств исходного объекта сделать массив объектов со значениями полей первого?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Рекурсия есть:

    const getPrimitives = data =>
      data instanceof Object
        ? Object.values(data).flatMap(getPrimitives)
        : [ data ];
    
    const result = getPrimitives(obj).map(value => ({ value }));

    или

    function* getPrimitives(data) {
      if (data instanceof Object) {
        for (const k in data) if (Object.hasOwn(data, k)) {
          yield* getPrimitives(data[k]);
        }
      } else {
        yield data;
      }
    }
    
    const result = Array.from(getPrimitives(obj), n => ({ value: n }));

    Рекурсии нет:

    function getNestedData(data, test, getter = n => n) {
      const result = [];
    
      for (const stack = [ data ]; stack.length;) {
        const n = stack.pop();
    
        if (n instanceof Object) {
          stack.push(...Object.values(n).reverse());
        }
    
        if (test(n)) {
          result.push(getter(n));
        }
      }
    
      return result;
    }
    
    const resutl = getNestedData(obj, n => n !== Object(n), n => ({ value: n }));
    Ответ написан
    Комментировать
  • Как убрать объекты дубликаты из массива?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Вот функция уникализации по нескольким параметрам:

    const unique = function*(data, keys = n => n) {
      const picked = new Map;
    
      for (const n of data) {
        const p = []
          .concat(keys(n))
          .reduce((acc, k) => acc.set(k, acc.get(k) ?? new Map).get(k), picked);
    
        if (!p.set(this, p.has(this)).get(this)) {
          yield n;
        }
      }
    }.bind(Symbol());

    А вот так её можно применять в вашем случае:

    // собираем новый массив
    const newArr = Array.from(unique(arr, n => [ n.name, n.value ]));
    
    // обновляем существующий
    arr.splice(0, arr.length, ...unique(arr, Object.values));
    Ответ написан
    1 комментарий
  • Как собрать массив из двух других массивов по совпадению 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'ы (ищем или опять же, если у вас там <form>, то доступна коллекция ссылок) и элемент, в который надо записать результат вычислений.

    document.addEventListener('input', e => {
      if (e.target.classList.contains('form-control')) {
        const form = e.target.closest('тут селектор формы');
        form.querySelector('span').innerText = Array.prototype.reduce.call(
          form.querySelectorAll('input.form-control'),
          (acc, n) => acc * (+n.value || 0),
          1
        );
      }
    });
    
    // или
    
    document.addEventListener('input', ({ target: t }) => {
      if (t.matches('.form-control')) {
        t.form.querySelector('span').textContent = Array
          .from(t.form.elements)
          .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
    ), {})

    или

    Object.fromEntries(arr
      .map(n => n.split(': ').pop().split(' = '))
      .filter(n => n.length === 2)
    )
    Ответ написан
    2 комментария
  • Как реализовать несколько независимых степперов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Как опознать кнопки +/-, как обновить значение:

    const plusSelector = '.count-plus';
    const minusSelector = '.count-minus';
    
    function updateCount(el, change) {
      const counterEl = el.closest('.count-wrapper').querySelector('.counter');
      counterEl.innerText = Math.max(0, +counterEl.innerText + change);
    }

    Слушать клики можно непосредственно на кнопках:

    function setClickHandler(selector, change) {
      document.querySelectorAll(selector).forEach(function(n) {
        n.addEventListener('click', this);
      }, e => updateCount(e.target, change));
    }
    
    setClickHandler(plusSelector, 1);
    setClickHandler(minusSelector, -1);

    Или на каком-нибудь их общем предке:

    document.addEventListener('click', ({ target: t }) => {
      const change = +t.matches(plusSelector) || -t.matches(minusSelector);
      if (change) {
        updateCount(t, change);
      }
    });
    Ответ написан
    Комментировать
  • Почему функция ничего не возвращает?

    0xD34F
    @0xD34F Куратор тега JavaScript
    return arr.forEach((fn) => {

    Понятно.

    Вот дураки вроде вас: раз дурак, два дурак, три дурак, четыре, пять.
    Ответ написан
    1 комментарий
  • Как с помощью JQUERY перебрать и сложить значение нескольких select?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Функция суммирования:

    function sum(data, key = n => n) {
      const getVal = key instanceof Function ? key : n => n[key];
      let result = 0;
    
      for (const n of data) {
        result += +getVal(n) || 0;
      }
    
      return result;
    }

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

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

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

    document.querySelector('form').addEventListener('change', function(e) {
      if (e.target.tagName === 'SELECT') {
        document.getElementById('result').value = sum(
          this.getElementsByTagName('select'),
          n => n.selectedOptions[0].getAttribute('value')
        );
      }
    });

    document.forms[0].onchange = ({ target: t }) => {
      if (t.matches('select')) {
        t.form.result.value = sum(
          t.form.querySelectorAll('option:checked:not(:disabled)'),
          n => n.attributes.value.value
        );
      }
    };
    Ответ написан
    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.prototype.reduce.call(table.rows, (acc, tr) => (
        Array.prototype.forEach.call(tr.cells, (n, i) => (acc[i] ??= []).push(n)),
        acc
      ), []);
    
      table.replaceChildren();
      cells.forEach(n => table.insertRow().append(...n));
    }

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

    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));
    Ответ написан
    Комментировать