Ответы пользователя по тегу JavaScript
  • Как скопировать определенные (не все) свойства объекта?

    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, ([ k, v ]) => 'abc'.includes(k) && v % 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.split(/(?<=\S)\s*(?=[А-ЯЁ])/).join(' ')
    
    // или
    
    str.replace(/(?<=\S)(?=[А-ЯЁ])/g, ' ')
    
    // или
    
    str.replace(/(\S)(?=[А-ЯЁ])/g, '$1 ')
    
    // или
    
    str.replace(/[А-ЯЁ]/g, (m, i) => i && str[~-i] !== ' ' ? ' ' + m : m)
    Ответ написан
    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: -~Object.keys(obj).at(-1) },
      (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 sorted = (arr, key) => arr
      .map(n => [ key(n), n ])
      .sort(([a], [b]) => a < b ? -1 : +(a > b))
      .map(n => n[1]);
    
    
    const sortedArr = sorted(arr, n => new Date(n.replace(/(\d+)\.(\d+)\.(\d+),/, '$3-$2-$1')));
    // или
    const sortedArr = sorted(arr, n => n.replace(/.+(?=,)/, m => m.split('.').reverse().join('')));
    Ответ написан
    Комментировать
  • Как из массива строк получить те, у которых длина равна 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
    const [ date = null ] = arr.map(n => n.date).sort();

    или

    const date = arr.reduce((min, { date: n }) => min !== null && min < n ? min : n, null);

    или

    const date = arr.length
      ? new Date(Math.min(...arr.map(n => new Date(n.date)))).toISOString()
      : null;
    Ответ написан
    Комментировать
  • Как найти сумму элементов вложенных массивов?

    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
    Странно, но все работает, несмотря на ошибку:

    TypeError: Cannot read properties of null (reading 'insertAdjacentHTML').

    Ничего странного, id существующих блоков совпадают с id первых объектов в массиве, т.е., сначала заполняются все существующие блоки, а уже потом случается ошибка, когда пытаетесь дёрнуть метод у отсутствующего блока.

    Какие есть способы победить ошибку:

    • Проверка наличия элемента перед тем как попытаться что-то в него добавить.

      Можно делать это явно:

      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>
          `;
        }
      });

      Или не очень:

      for (const { id, title, body } of arr) {
        document.querySelector(`[id="${id}"]`)?.insertAdjacentHTML('beforeend', `
          <h2>Id: ${id}</h2>
          <h3>${title}</h3>
          <p>${body}</p>
        `);
      }

    • Создание элементов под все полученные объекты (в этом случае пусть section изначально будет пустым):

      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('');

    • Ограничение объёма получаемых данных.

      Если id элементов действительно имеют показанный вами вид (начинаются с 1 и идут по возрастанию), то просто запрашиваем столько объектов, сколько есть элементов:

      const limit = document.querySelector('section').children.length;
      const requestURL = `https://jsonplaceholder.typicode.com/posts?_limit=${limit}`;

      Или, можно запросить объекты с id как у элементов:

      const requestURL = 'https://jsonplaceholder.typicode.com/posts?' + Array
        .from(document.querySelector('section').children, n => `id=${n.id}`)
        .join('&');


    Какой из способов следует предпочесть? 2 + 3. Только, конечно, количество объектов или их id надо брать не из DOM.
    Ответ написан
  • Как сложить значения 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 комментария
  • Как сделать сортировку HTML объектов в зависимости от чисел внутри элемента?

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

    const sorted = (data, key) => Array
      .from(data, n => [ key(n), n ])
      .sort(([a], [b]) => a < b ? -1 : +(a > b))
      .map(n => n[1]);

    Как отсортировать содержимое DOM-элемента - да просто добавить ему это содержимое в отсортированном виде:

    const sortChildren = (el, key) =>
      el.append(...sorted(el.children, key));

    Направление сортировки будем указывать с помощью data-атрибута:

    <button data-order="-1">От большего к меньшему</button>
    <button data-order="+1">От меньшего к большему</button>

    Кнопкам назначаем общий обработчик клика, где сортируем содержимое .catalog-items (вес элемента - число внутри, умноженное на направление сортировки), кликнутой кнопке класс добавляем, у остальных убираем:

    const wrapper = document.querySelector('.catalog-items');
    const buttons = document.querySelectorAll('[data-order]');
    
    buttons.forEach(n => n.addEventListener('click', onClick));
    
    function onClick({ target: t }) {
      const order = +t.dataset.order;
      sortChildren(wrapper, el => parseInt(el.innerText) * order);
      buttons.forEach(n => n.classList.toggle('active', n === t));
    }
    Ответ написан
    3 комментария
  • Как выполнять вычисления с помощью функций (результат вызова предыдущей должен быть аргументом следующей)?

    0xD34F
    @0xD34F Куратор тега JavaScript
    "Числовые" функции проверяют, является ли переданное им значение функцией, если да - вызывают её со своим числом в качестве аргумента, нет - просто возвращают число.

    Функции операций принимают второе число, возвращают функцию, принимающую первое число и производящую над переданными числами операцию.

    const [ zero, one, two, three, four, five, six, seven, eight, nine ] = Array.from(
      { length: 10 },
      (_, i) => f => f instanceof Function ? f(i) : i
      // или
      // (_, i) => f => f?.(i) ?? i
    );
    
    const plus = a => b => b + a;
    const minus = a => b => b - a;
    const times = a => b => b * a;
    const dividedBy = a => b => b / a | 0;
    Ответ написан
    Комментировать
  • Как реализовать функцию, которая принимает любое неотрицательное целое число и возвращает его цифрами в порядке убывания?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const sortDigits = num => +[...`${num}`].sort((a, b) => b - a).join('');

    или

    const sortDigits = num => ''.concat.apply('', num.toString().split('').sort().reverse()) - 0;

    или

    const sortDigits = num => Number(Array
      .from('' + num)
      .reduce((acc, n) => (acc[n]++, acc), Array(10).fill(0))
      .reduceRight((acc, n, i) => acc + String(i).repeat(n), '')
    );

    или

    const sortDigits = num => Array
      .prototype
      .reduce
      .call(String(num), (acc, n) => ((acc[9 - n] ??= []).push(n | 0), acc), [])
      .flat()
      .reduce((acc, n) => acc * 10 + n, 0);
    Ответ написан
    1 комментарий