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

    0xD34F
    @0xD34F Куратор тега JavaScript
    function parallel(tasks, onAllTasksComplete) {
      const results = [];
      let numCompleted = 0;
    
      function onTaskComplete(index, result) {
        results[index] = result;
        if (++numCompleted === tasks.length) {
          onAllTasksComplete(results);
        }
      }
    
      for (let i = 0; i < tasks.length; i++) {
        const onComplete = r => onTaskComplete(i, r);
        const result = tasks[i](onComplete);
        if (result !== undefined) {
          onComplete(result);
        }
      }
    }
    Ответ написан
    Комментировать
  • Последовательное выполнение действий в массиве?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const process = (funcs, initialArg) => funcs.reduce((arg, f) => f(arg), initialArg);
    Ответ написан
  • Как получить месяц в родительном падеже?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Получить месяц с числом, число обрезать:

    const getMonthNameInGenitiveCase = (date = new Date) =>
      date.toLocaleString('ru', {
        month: 'long',
        day: 'numeric',
      }).split(' ')[1];

    Или, сделать массив с месяцами, выбирать нужный элемент:

    const getMonthNameInGenitiveCase = (date = new Date) =>
      [
        'января', 'февраля', 'марта', 'апреля', 'мая', 'июня',
        'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря',
      ][date.getMonth()];
    Ответ написан
  • Как правильно сделать модальное окно?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const openSelector = '[data-target]';
    const closeSelector = '.modal__close';
    const modalSelector = '.modal';
    const activeClass = 'modal--active';

    Делегирование, общий обработчик клика назначается один раз для всех:

    document.addEventListener('click', ({ target: t }) => {
      const open = t.closest(openSelector);
      if (open) {
        document.querySelector(`#${open.dataset.target}`).classList.add(activeClass);
      } else {
        t.closest(closeSelector)?.closest(modalSelector).classList.remove(activeClass);
      }
    });

    Отдельные обработчики для кнопок открытия и закрытия, назначаются каждой кнопке индивидуально:

    const onClick = (selector, handler) => document
      .querySelectorAll(selector)
      .forEach(n => n.addEventListener('click', handler));
    
    onClick(openSelector, ({ currentTarget: { dataset: { target } } }) => {
      document.querySelector(`#${target}`).classList.add(activeClass);
    });
    
    onClick(closeSelector, e => {
      e.currentTarget.closest(modalSelector).classList.remove(activeClass);
    });
    Ответ написан
    Комментировать
  • Как переменная одновременно может быть и true, и false?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Можно сделать объект, у которого будет определено преобразование в примитив, изменяющее состояние объекта, от которого его примитивное представление зависит:

    const obj = {
      val: 1,
      valueOf() {
        return this.val ^= 1;
      },
    };
    
    // или
    
    const obj = {
      val: '1',
      toString() {
        return this.val = '1'.slice(this.val.length);
      },
    };
    
    // или
    
    const obj = {
      val: true,
      [Symbol.toPrimitive]() {
        return this.val = !this.val;
      },
    };

    console.log(obj == false, obj == true); // true true
    console.log(...Array.from({ length: 5 }, () => +obj)); // 0 1 0 1 0
    Ответ написан
    4 комментария
  • Как удалить лишние элементы из массива?

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

    function mustStay(n) {
      for (const m of arr2) {
        if (m.content.insuranceType === n.value) {
          return false;
        }
      }
    
      return true;
    }
    
    // или
    
    const mustStay = n => arr2.every(m => m.content.insuranceType !== n.value);
    
    // или
    
    const mustStay = function(n) {
      return !this.has(n.value);
    }.bind(new Set(arr2.map(n => n.content.insuranceType)));
    
    // или
    
    const mustStay = n =>
      (function next(i) {
        return i >= arr2.length
          ? true
          : arr2[i].content.insuranceType !== n.value && next(-~i);
      })(0);

    Удаляем неподходящие:

    for (let i = 0; i < arr1.length; i++) {
      if (!mustStay(arr1[i])) {
        for (let j = i--; ++j < arr1.length; arr1[j - 1] = arr1[j]) ;
        arr1.pop();
      }
    }
    
    // или
    
    arr1.reduceRight((_, n, i, a) => mustStay(n) || a.splice(i, 1), null);
    
    // или
    
    arr1.splice(0, arr1.length, ...arr1.filter(mustStay));
    
    // или
    
    arr1.length -= arr1.reduce((acc, n, i, a) => (
      a[i - acc] = n,
      acc + !mustStay(n)
    ), 0);
    Ответ написан
    1 комментарий
  • Как по клику на кнопку переходить к следующему вопросу?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const steps = [ /* ... */ ];
    let iStep = 0;
    
    const question = document.querySelector('.quiz__question');
    const answers = document.querySelector('.quiz__answers');
    
    const prev = document.querySelector('.quiz__button_prev');
    const next = document.querySelector('.quiz__button_next');
    
    prev.addEventListener('click', () => nextStep(-1));
    next.addEventListener('click', () => nextStep(+1));
    
    function nextStep(stepChange) {
      iStep = Math.max(0, Math.min(steps.length - 1, iStep + stepChange));
    
      prev.disabled = iStep === 0;
      next.disabled = iStep === steps.length - 1;
    
      question.innerHTML = steps[iStep].question;
      answers.innerHTML = steps[iStep].answers.map(n => `<li>${n}</li>`).join('');
    }
    
    nextStep(0);
    Ответ написан
    1 комментарий
  • Как отфильтровать карточки в зависимости от выбора?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Фильтрам добавить data-атрибуты, указывающие, по какому свойству надо отбирать карточки:

    <div data-filter="service">
      <div class="sign">Streaming service</div>
      <div class="item">Netflix</div>
      <div class="item">HBO Max</div>
      <div class="item">Hulu</div>
    </div>
    <div data-filter="genre">
      <div class="sign">Movie genre</div>
      <div class="item">Comedy</div>
      <div class="item">Action</div>
      <div class="item">Horror</div>
      <div class="item">Drama</div>
      <div class="item">Fantasy</div>
    </div>

    Эти свойства в виде опять же data-атрибутов добавить карточкам:

    <li class="card" data-service="Netflix" data-genre="Comedy">

    Собственно фильтрация:

    const cardSelector = '.card';
    const hiddenCardClass = 'hidden';
    const filterSelector = '[data-filter]';
    const filterItemSelector = `${filterSelector} .item`;
    const activeFilterItemClass = 'item_active';
    
    document.querySelector('.filters').addEventListener('click', e => {
      const item = e.target.closest(filterItemSelector);
      if (!item) {
        return;
      }
    
      item.closest(filterSelector).querySelectorAll(filterItemSelector).forEach(n => {
        n.classList[n === item ? 'toggle' : 'remove'](activeFilterItemClass);
      });
    
      const values = Array.from(
        e.currentTarget.querySelectorAll(`.${activeFilterItemClass}`),
        n => [ n.closest(filterSelector).dataset.filter, n.innerText ]
      );
    
      document.querySelectorAll(cardSelector).forEach(n => {
        n.classList.toggle(hiddenCardClass, values.some(m => n.dataset[m[0]] !== m[1]));
      });
    });
    Ответ написан
    Комментировать
  • Что нужно вернуть, чтобы прекратить рекурсию?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Не "что", а "в каком случае".

    return getValue(objectValus[prop], property);

    Надо не тупо возвращать результат рекурсивного вызова, а сначала проверять, что там не undefined, например.

    А вообще, можно и попроще сделать:

    const getValue = (obj, key) => Object
      .values(obj ?? {})
      .reduce((found, n) => found ?? getValue(n, key), obj?.[key]);
    Ответ написан
    Комментировать
  • Почему jquery не возвращает все элементы с классом?

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

    0xD34F
    @0xD34F Куратор тега JavaScript
    const total = results.reduce((acc, n) => acc + n.counts, 0);
    const resultsObj = Object.fromEntries(results.map(n => [ n.id, n.counts ]));
    const usersWithResults = users.map(n => ({
      ...n,
      result: (resultsObj[n.id] ?? 0) / total * 100,
    }));
    Ответ написан
    Комментировать
  • Как сосчитать количество повторений свойств в массиве объектов?

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

    const result = Object.values(arr.reduce((acc, { id }) => (
      (acc[id] ??= { id, count: 0 }).count++,
      acc
    ), {}));

    Или, решаем задачу в более общем виде:

    function uniqueWithCount(data, key, countKey) {
      const getKey = key instanceof Function ? key : n => n[key];
      const unique = new Map;
    
      for (const n of data) {
        const k = getKey(n);
        unique
          .set(k, unique.get(k) ?? { ...n, [countKey]: 0 })
          .get(k)[countKey]++;
      }
    
      return unique;
    }

    const result = Array.from(
      uniqueWithCount(arr, 'id', 'count').values(),
      ({ id, count }) => ({ id, count })
    );
    Ответ написан
    2 комментария
  • Ошибка в коде JS (задача на codewars)?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Да без понятия, в чём там у вас проблема. Что за ошибка - об этом вы почему-то предпочли умолчать. Так что, какой вопрос - такой ответ. Вот вам решения рабочие, сравнивайте со своим, думайте:

    Можно собирать новый массив:

    function solve(arr) {
      const last = arr.reduce((acc, n, i) => (acc[n] = i, acc), {});
      return arr.filter((n, i) => last[n] === i);
    }

    Или удалять элементы из существующего:

    function solve(arr) {
      const last = new Map(arr.map((n, i) => [ n, i ]));
    
      arr.length -= arr.reduce((acc, n, i, a) => (
        a[i - acc] = n,
        acc + (i !== last.get(n))
      ), 0);
    
      return arr;
    }

    Или и то и другое сразу:

    const solve = arr => (
      arr.splice(0, arr.length, ...[...new Set(arr.reverse())].reverse()),
      arr
    );
    Ответ написан
    3 комментария
  • Как соединить 2 массива чередуя элементы через 5?

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

    const merge = (arr1, arr2, step) => Array
      .from(
        { length: Math.min(arr1.length, 1 + arr2.length / step | 0) },
        (_, i) => [ ...arr2.slice(~-i * step, i * step), arr1[i] ]
      )
      .flat();

    или

    const merge = (arr1, arr2, step) =>
      [].concat(...Array.from(
        { length: Math.min(arr1.length, 1 + arr2.length / step | 0) },
        (_, i) => [ arr1[i], ...arr2.slice(i * step, -~i * step) ]
      ));

    или

    const merge = (arr1, arr2, step) =>
      Array.prototype.flatMap.call(arr1, (n, i) => [
        ...Object.assign(
          i ? Array(step).fill(null) : [],
          arr2.slice((i - 1) * step, i * step)
        ),
        n,
      ]);

    или

    function merge(arr1, arr2, step) {
      const len = arr2.length;
      return Array.from(
        { length: len - (len % step) + 1 + len / step | 0 },
        (_, i) => {
          const j = i / (step + 1) | 0;
          return i % (step + 1)
            ? arr2[i - j - 1]
            : arr1[j] ?? null
        });
    }

    Для вашего случая - const c = merge(b, a, 5); - можете воспользоваться любой из этих функций, результаты выдадут одинаковые. Чего может и не быть с другими аргументами. Например:

    Во втором массиве недостаточно элементов:

    merge('ABCD', [ 1, 2, 3, 4, 5, 6, 7 ], 4)
    
    // первая и четвёртая функции останавливаются на элементе первого массива,
    // если во втором массиве не хватает элементов, на заполнение
    // очередного промежутка между элементами первого массива
    ['A', 1, 2, 3, 4, 'B']
    
    // вторая функция остановится после того,
    // как отдаст все элементы второго массива
    ['A', 1, 2, 3, 4, 'B', 5, 6, 7]
    
    // третья функция подставит дефолтные значения
    // вместо недостающих элементов второго массива
    ['A', 1, 2, 3, 4, 'B', 5, 6, 7, null, 'C', null, null, null, null, 'D']

    Или, в первом массиве недостаточно элементов:

    merge('ABC', [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ], 2)
    
    // 1, 3
    ['A', 1, 2, 'B', 3, 4, 'C']
    
    // 2
    ['A', 1, 2, 'B', 3, 4, 'C', 5, 6]
    
    // 4
    ['A', 1, 2, 'B', 3, 4, 'C', 5, 6, null, 7, 8, null]
    Ответ написан
    Комментировать
  • Как присвоить функцию с аргументами, не вызывая её, лучше?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Ничего не лучше - показанные куски кода делают разное. Откройте консоль, выполните

    (function() {
      function func(arg) {
        console.log(arg, this);
      }
    
      let arg = 1;
    
      const f1 = () => func(arg);
      const f2 = func.bind(this, arg);
    
      arg = 2;
    
      f1();
      f2();
    }).call('hello, world!!');

    , подумайте над полученными результатами.

    Сами решайте, чего вам надо.
    Ответ написан
    Комментировать
  • Как изменить числа в строке на 0, если меньше 5 и на 1, если больше 5?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Array.from(str, n => n < 5 ? 0 : 1).join('')
    
    // или
    
    [...str].reduce((acc, n) => acc + +(n >= 5), '')
    
    // или
    
    ''.concat(...str.split('').map(n => Math.floor(n / 5)))
    
    // или
    
    [].map.call(str, n => -~-~-~n >> 3).join``
    
    // или
    
    str.replace(/./g, m => Number('56789'.includes(m)))
    
    // или
    
    str.replace(/[1-4]/g, 0).replace(/[5-9]/g, 1)
    Ответ написан
    Комментировать
  • Как попарно соединить два объекта в массиве?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const newArr = Array.from(
      { length: arr.length / 2 },
      (n, i) => ({ ...arr[i], ...arr[i + arr.length / 2] })
    );

    Но всегда ли количество ключей будет равно двум? - лучше посчитаем самостоятельно, сколько их там:

    const numKeys = new Set(arr.flatMap(Object.keys)).size;
    const newArr = arr.reduce((acc, n, i, a) => (
      Object.assign(acc[i % (a.length / numKeys)] ??= {}, n),
      acc
    ), []);
    
    // или
    
    const numKeys = Object.keys(Object.assign({}, ...arr)).length;
    const numObjs = arr.length / numKeys;
    const newArr = Array.from(
      { length: numObjs },
      (n, i) => Object.assign({}, ...Array.from(
        { length: numKeys },
        (m, j) => arr[j * numObjs + i]
      ))
    );

    Или можно считать какой ключ сколько раз встретился, и добавлять свойство соответствующему элементу собираемого массива:

    const newArr = arr.reduce((acc, n) => {
      const [ [ k, v ] ] = Object.entries(n);
      const i = acc[0][k] = (acc[0][k] ?? -1) + 1;
      (acc[1][i] ??= {})[k] = v;
      return acc;
    }, [ {}, [] ])[1];
    Ответ написан
    5 комментариев
  • Как поменять местами соседние элементы в массиве?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Меняем на месте:

    for (let i = 1; i < arr.length; i += 2) {
      [ arr[i], arr[i - 1] ] = [ arr[i - 1], arr[i] ];
    }

    Создаём новый массив:

    const newArr = arr.map((n, i, a) => a[i - (i & 1 || -1)] ?? n);
    Ответ написан
    Комментировать
  • Почему не работают якорные переходы на JS, если внутри тега прописать еще один тег?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Потому что целевым элементом будет уже не <a>. Соответственно, не будет и атрибута href. А у null не будет метода substr. О чём вы должны были узнать сами, из сообщения об ошибке - если бы открыли консоль.

    Вместо target следует использовать currentTarget.

    Или, можно сделать делегированный обработчик клика, в котором пытаться подняться от target к <a>, если таковой нашёлся, получать его href, начинается с решётки - выполнять плавную прокрутку к соответствующему элементу:

    document.addEventListener('click', e => {
      const href = e.target.closest('a')?.getAttribute('href') ?? '';
      if (href[0] === '#') {
        e.preventDefault();
        document.querySelector(href)?.scrollIntoView({
          behavior: 'smooth',
        });
      }
    });
    Ответ написан
    Комментировать
  • Как с помощью JS или PHP обрезать последнюю часть url?

    0xD34F
    @0xD34F Куратор тега JavaScript
    JS:

    const newUrl = url.match(/.+\//)[0];
    
    // или
    
    const newUrl = url.replace(/[^\/]+$/, '');
    
    // или
    
    const newUrl = url.slice(0, url.lastIndexOf('/') + 1);
    
    // или
    
    const newUrl = url.split(/(\/)/).slice(0, -1).join('');

    PHP:

    preg_match('/.+\//', $url, $match);
    $newUrl = $match[0];
    
    // или
    
    $newUrl = preg_replace('/[^\/]+$/', '', $url);
    
    // или
    
    $newUrl = substr($url, 0, strrpos($url, '/') + 1);
    
    // или
    
    $newUrl = implode('', array_slice(preg_split('/(\/)/', $url, 0, PREG_SPLIT_DELIM_CAPTURE), 0, -1));
    Ответ написан
    2 комментария