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

    0xD34F
    @0xD34F Куратор тега JavaScript
    const groupSum = (arr, idKey, ...sumKeys) =>
      Object.values(arr.reduce((acc, n) => (
        acc[n[idKey]] ??= sumKeys.reduce((group, k) => (group[k] = 0, group), { ...n }),
        sumKeys.forEach(k => acc[n[idKey]][k] += n[k]),
        acc
      ), {}));
    
    
    const result = groupSum(arr, 'product', 'price', 'selling_price', 'quantity');
    Ответ написан
    2 комментария
  • Как по клику получить все data-id которые были выбраны с помощью счетчика в заданном количестве?

    0xD34F
    @0xD34F Куратор тега JavaScript
    [
      [     '.btn-all', 'click',   onGetDataClick ],
      [ '.elem button', 'click', onPlusMinusClick ],
      [      '.amount', 'input',    onAmountInput ],
    ].forEach(([ selector, type, handler ]) => {
      document.querySelectorAll(selector).forEach(n => n.addEventListener(type, handler));
    });
    
    function onGetDataClick(e) {
      const data = Array.prototype.flatMap.call(
        e.target.closest('.child-block').querySelectorAll('.elem'),
        n => Array(+n.querySelector('.amount').value).fill(+n.dataset.id)
      );
    
      console.log(data);
    }
    
    function onPlusMinusClick({ target }) {
      const input = target.closest('.elem').querySelector('.amount');
      input.value -= target.innerText === '+' ? -1 : 1;
      input.dispatchEvent(new Event('input'));
    }
    
    function onAmountInput({ target: t }) {
      t.value = Math.max(t.min, Math.min(t.max, t.value | 0));
    }
    Ответ написан
    3 комментария
  • Как в один ключ передать два и более ключа?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const obj = {
      a: {
        b: {
          c: {
            xxx: 666,
          },
        },
      },
    };
    
    const proxy = new Proxy(obj, {
      get(target, key) {
        return key.split('.').reduce((p, c) => p?.[c], target);
      },
      set(target, key, val) {
        const keys = key.split('.');
        const k = keys.pop();
        keys.reduce((p, c) => p[c] ??= {}, target)[k] = val;
        return true;
      },
    });
    
    console.log(proxy['a.b.c.xxx']); // 666
    proxy['x.y.z'] = 187;
    console.log(obj.x.y.z); // 187
    Ответ написан
    4 комментария
  • Как скопировать определенные (не все) свойства объекта?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Если в новом объекте должны быть все указанные свойства, вне зависимости от их наличия в исходном объекте:

    // имена свойств можно передавать в виде массива
    const pick = (obj, keys) => Object.fromEntries(keys.map(n => [ n, obj[n] ]));
    const newObj = pick(obj, [ 'b', 'c' ]);
    
    // или как отдельные значения
    const pick = (obj, ...keys) => keys.reduce((acc, n) => (acc[n] = obj[n], acc), {});
    const newObj = pick(obj, 'b', 'c');

    Если отсутствующие свойства не нужны:

    const pickExisting = (obj, keys) =>
      Object.fromEntries(keys.filter(n => n in obj).map(n => [ n, obj[n] ]));

    Если нужны только собственные свойства:

    const pickOwn = (obj, keys) =>
      keys.reduce((acc, n) => (Object.hasOwn(obj, n) && (acc[n] = obj[n]), acc), {});

    Если хотим выбирать свойства не только по именам, но и по значениям:

    const pick = (obj, filterFn) => Object.fromEntries(Object.entries(obj).filter(filterFn));
    
    const obj1 = pick(obj, ([ key, val ]) => 'abc'.includes(key) && val % 2); // {a: 1, c: 3}
    const obj2 = pick(obj, n => n[1] > 2); // {c: 3, d: 5}
    Ответ написан
    Комментировать
  • Не работает перебор элементов в классе. Что делать?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Такие ваши варианты:

    1. nextSlide() { ---> nextSlide = () => {
    2. .onclick = this.nextSlide; ---> .onclick = () => this.nextSlide();
    3. .onclick = this.nextSlide; ---> .onclick = this.nextSlide.bind(this);

    Ну и конечно же бегом гуглить, разбираться - когда какое значение имеет this.
    Ответ написан
  • Как с помощью регулярного выражения отделить символы с большой буквой, добавив пробел?

    0xD34F
    @0xD34F Куратор тега JavaScript
    str.replace(/(?<=\S)(?=[А-ЯЁ])/g, ' ')

    или

    str.replace(/(\S)([А-ЯЁ])/g, '$1 $2')
    Ответ написан
    1 комментарий
  • Почему filter() возвращает пустой массив?

    0xD34F
    @0xD34F Куратор тега JavaScript
    .filter(function (el, i) {
      return arr[i] === el[i];
    })

    Элементы массива - числа. Как думаете, бывает ли у чисел свойство 0? Или 5? А зачем тогда [i]? Может, должно было быть arr[i] === el?
    Ответ написан
    1 комментарий
  • Как из объекта с ключами-числами сделать массив?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const arr = Array.from(
      { length: 1 + +Object.keys(obj).pop() },
      (n, i) => obj[i] ?? null,
    );

    или

    const arr = Object.entries(obj).reduce((acc, n) => (
      acc.push(...Array(n[0] - acc.length).fill(null), n[1]),
      acc
    ), []);
    Ответ написан
    Комментировать
  • Как объединить две функции в одну?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const searchOptions = [
      [ '#search_table', tr => $('input[name^=name]', tr).val() ],
      [ '#search_card', tr => $('[data-item-name="name"]', tr).text() ]
    ];
    
    $(searchOptions.map(n => n[0]).join(', ')).on('input', () => {
      const values = searchOptions.map(n => $(n[0]).val().toLowerCase());
    
      $('#tabname tbody tr')
        .hide()
        .filter((i, n) => searchOptions.every((m, j) => m[1](n).toLowerCase().includes(values[j])))
        .show();
    });
    Ответ написан
  • Как сделать сортировку дат в toLocaleString виде?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const sortedArr = arr
      .map(n => [ n, +new Date(n.replace(/(\d+)\.(\d+)\.(\d+),/, '$3-$2-$1')) ])
      .sort((a, b) => a[1] - b[1])
      .map(n => n[0]);
    Ответ написан
    Комментировать
  • Как из массива строк получить те, у которых длина равна 5?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Дурацкому вопросу - дурацкий ответ:

    `${arr}`.match(/\b\w{5}\b/g) || []
    
    // или
    
    arr.reduce((acc, n) => (n.length ^ '0b101' || acc.push(n), acc), [])
    
    // или
    
    arr.filter(n => n[4] && !n[-~4])
    
    // или
    
    arr.filter(RegExp.prototype.test.bind(/^.....$/))
    
    // или
    
    arr.reduce((acc, n) => ((acc[n.search('$')] ??= []).push(n), acc), {})[5] ?? []
    
    // или
    
    (function xxx(arr, i = 0) {
      return arr.hasOwnProperty(i)
        ? [].concat(5 - [].push(...arr[i]) ? [] : arr[i], xxx(arr, i + 1))
        : [];
    })(arr)
    Ответ написан
    Комментировать
  • Как оптимизировать нерекурсивный обход дерева?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Что значит оптимизировать? Не хардкодить ключи? Уменьшить количество проверок? Всё просто: вложенные данные, а значит и необходимость что-то закинуть в стек, может случиться только при обработке объектов, так что вот это и надо проверять - что текущее значение, оно instanceof Object.

    Какие значения должны попадать в массив с результатами - этот вопрос тоже можно решить в более общем виде. Пусть это будет параметр - функция, принимающая значение и возвращающая true или false.

    Сортировку следует убрать. Если результаты нужны именно в отсортированном виде, то сортируйте их снаружи, нечего в одной функции решать несколько задач. Если же сортировка применяется для "исправления" результатов, то кривые они потому, что вы забыли, что данные из стека извлекаются в обратном порядке, т.е., надо изменить порядок добавления данных в стек.

    Короче вот, "оптимизировано":

    function getNestedData(data, test) {
      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(n);
        }
      }
    
      return result;
    }
    
    
    console.log(getNestedData(tree, Number.isFinite));
    Ответ написан
    Комментировать
  • Как найти в строке первый уникальный символ без учёта регистра?

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

    const firstNonRepeatingLetter = ([...str]) => Object
      .values(str.reduce((acc, n, i) => ((acc[n.toLowerCase()] ??= [ 0, i, n ])[0]++, acc), {}))
      .reduce((min, n) => (n[0] === 1 && n[1] < min[1] ? n : min), [ 0, Infinity, '' ])
      .pop();

    Или. Приводим строку к нижнему регистру; ищем индекс первого уникального символа, т.е. такого, у которого индекс первого вхождения в строку равен индексу последнего вхождения; по найденному индексу извлекаем из исходной строки символ:

    const firstNonRepeatingLetter = str =>
      str.charAt(Array
        .from(str.toLowerCase())
        .findIndex((n, i, a) => a.indexOf(n) === a.lastIndexOf(n))
      );
    Ответ написан
    Комментировать
  • Как отправлять запрос каждые N секунд, пока в ответ не получу нужный статус?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Асинхронная функция, внутри бесконечный цикл, в цикле запрос. Если результат запроса нас устраивает - выходим из цикла. Задержка между итерациями цикла реализуется с помощью промиса, который резолвится по таймауту.

    async function() {
      let result = null;
    
      while (1) {
        result = await fetch(...);
    
        if (result тот, который нужен) {
          break;
        }
    
        await new Promise(r => setTimeout(r, 5000));
      }
    
      return result;
    }
    Ответ написан
    6 комментариев
  • Как получить самую давнюю дату, хранящуюся в массиве?

    0xD34F
    @0xD34F Куратор тега JavaScript
    arr.map(n => n.date).sort()[0]
    
    // или
    
    arr.reduce((min, n) => min < n.date ? min : n.date)
    
    // или
    
    new Date(Math.min(...arr.map(n => new Date(n.date)))).toISOString()
    Ответ написан
    Комментировать
  • Как найти сумму элементов вложенных массивов?

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

    const sum = data =>
      Array.isArray(data)
        ? data.reduce((acc, n) => acc + sum(n), 0)
        : (+data || 0);

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

    function sum(data) {
      let result = 0;
    
      for (const stack = [ data ]; stack.length; ) {
        const n = stack.pop();
        if (n instanceof Array) {
          stack.push(...n);
        } else {
          result += +n || 0;
        }
      }
    
      return result;
    }

    Здесь, конечно, будет складываться всё, что можно сложить. Т.е., не только собственно числа, но и всё, что может быть сведено к числу, например true, '0xBB', { valueOf: () => 666 } и т.д.

    Если нужно складывать только числа - предлагаю подумать самостоятельно, как изменить представленный код.
    Ответ написан
    1 комментарий
  • Как вывести элементы массива в разные блоки по ID?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Проверяйте наличие элементов:

    function displayData(arr) {
      arr.forEach(n => {
        const el = document.getElementById(n.id);
        if (el) {
          el.innerHTML = `
            <h2>Id: ${n.id}</h2>
            <h3>${n.title}</h3>
            <p>${n.body}</p>
          `;
        }
      });
    }

    Или, если надо вывести всё, создавайте их:

    function displayData(arr) {
      document.querySelector('section').innerHTML = arr
        .map(n => `
          <div id="post-${n.id}">
            <h2>Id: ${n.id}</h2>
            <h3>${n.title}</h3>
            <p>${n.body}</p>
          </div>`)
        .join('');
    }
    Ответ написан
  • Как сложить значения input'ов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const elements = document.querySelectorAll('[name^=AR_AMOUNT]');

    Нормальный способ - перебираем коллекцию элементов (цикл в явном виде или с помощью методов массива), достаём значения, складываем:

    const sum = Array.prototype.reduce.call(
      elements,
      (acc, n) => acc + +n.value,
      0
    );
    
    // или
    
    let sum = 0;
    for (const { value } of elements) {
      sum += Number(value);
    }

    Ненормальный способ - рекурсия. Функция получает коллекцию элементов и индекс, если элемент с указанным индексом существует, возвращаем его значение плюс результат рекурсивного вызова с индексом, увеличенным на единицу; если элемента нет - возвращаем 0:

    const sum = (function sum(arr, i) {
      return arr[i] ? parseFloat(arr[i].value) + sum(arr, i + 1) : 0;
    })(elements, 0);

    Дикий способ - сами ничего считать не будем. Выдёргиваем из коллекции элементов значения; склеиваем их, используя в качестве разделителя символ +, в строку; строку отдаём в eval; всё, сумма получена (ну, почти, если исходная коллекция элементов была пуста, то строка тоже будет пустой, так что надо не забыть подставить 0 вместо возможного undefined, который является результатом выполнения пустой строки):

    const sum = eval(Array.from(elements, n => n.value).join('+')) ?? 0;
    Ответ написан
    Комментировать
  • Как через цикл вложить элементы DOM один в другой?

    0xD34F
    @0xD34F Куратор тега JavaScript
    main.innerHTML = arr.reduceRight((acc, n) => `<${n}>${acc}</${n}>`, '');
    Ответ написан
    Комментировать
  • Как объединить несколько объектов в один?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const merge = (target, ...sources) =>
      sources.reduce((acc, n) => (
        Object.entries(n).forEach(([ k, v ]) =>
          acc[k] = v instanceof Object
            ? merge(acc[k] instanceof Object ? acc[k] : {}, v)
            : v
        ),
        acc
      ), target);
    
    
    const result = merge({}, ...arrayObj);
    Ответ написан
    3 комментария