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

    0xD34F
    @0xD34F Куратор тега JavaScript
    Собрать новый массив:

    const newData = data.map((n, i, a) => ({
      ...n,
      newSick: n.sick - (i < ~-a.length && a[-~i].sick),
    }));

    Добавить свойство элементам существующего массива:

    data.forEach((n, i, a) => n.newSick = n.sick - (a[i + 1]?.sick ?? 0));
    // или
    data.reduceRight((prev, n) => (n.newSick = n.sick - prev, n.sick), 0);
    Ответ написан
    Комментировать
  • Как отсортировать массив пар [ключ, значение]?

    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 => n[0].replace(/^\D+/, ''));

    Если же значения могут быть не только двухзначными, то сравнивать их как строки уже нельзя. Делаем из них настоящие числа, считаем сумму; если второе слагаемое отсутствует, используем первое повторно:

    const sortedArr = sorted(arr, n => {
      const d = n[0].match(/\d+/g);
      return +d[0] + +d.at(-1);
    });
    Ответ написан
    Комментировать
  • Как добавить или отнять 1 по клику на кнопки?

    0xD34F
    @0xD34F Куратор тега JavaScript
    let count = 0;
    const countEl = document.querySelector('.text');
    const buttons = [...document.querySelectorAll('.click')];
    const onClick = e => updateCount(e.target.classList.toggle('clicked') ? 1 : -1);
    const updateCount = change => countEl.innerText = count += change;
    buttons.forEach(n => n.addEventListener('click', onClick));
    updateCount(buttons.reduce((acc, n) => acc + n.classList.contains('clicked'), 0));
    Ответ написан
    1 комментарий
  • Как найти произведения элементов массива и сумм столбцов матрицы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const result = arr.map((n, i) => n * matrix.reduce((acc, row) => acc + row[i], 0));

    или

    const result = Array(arr.length).fill(0);
    
    for (const row of matrix) {
      for (const [ i, n ] of row.entries()) {
        result[i] += n * arr[i];
      }
    }
    Ответ написан
    Комментировать
  • Как вывести результат "на лету"?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Вместо click'а у кнопки обрабатывайте input у формы:

    - document.querySelector('.calculate').addEventListener('click', function () {
    + document.querySelector('form').addEventListener('input', function () {

    UPD. Сам обработчик - как-то не круто всё выглядит:

    let select = document.querySelector('select').selectedIndex;
    let selectValue = select + 1; // selectedIndex считается с 0, поэтому +1

    Зачем индекс? У option'ов указаны value, так что можно смотреть значение select'а.

    let dateStart = document.querySelector('.start_date').value;
    let dateEnd = document.querySelector('.end_date').value;

    Ради получения значений нет необходимости искать элементы - ссылки на них есть в свойстве elements формы, в качестве ключа выступают значения атрибутов id и name. Также можно создать FormData, и доставать нужное уже оттуда.

    let out_3 = document.querySelector('.out_3');

    Аналогично - искать элемент не надо, ссылка на output внутри него есть в elements формы.

    for (let i = dateStart; i <= dateEnd; i = i + 24 * 60 * 60 * 1000) {

    Цикл не нужен. Вычитаем одну дату из другой, делим на количество миллисекунд в дне, получаем таким образом количество дней.

    Исправляем:

    document.querySelector('form').addEventListener('input', function() {
      const { elements } = this;
      const data = new FormData(this);
      const keys = [ 'enterDate', 'outDate', 'countRooms', 'prepayment' ];
    
      const basePayment = 2000;
      const [ enter, out, rooms, prepayment ] = keys.map(k => data.get(k) /* или elements[k].value */);
      const days = enter && out && enter < out
        ? (new Date(out) - new Date(enter)) / (24 * 60 * 60 * 1000)
        : 0;
    
      elements.payment.textContent = `${basePayment * days * rooms - prepayment} р`;
    });
    Ответ написан
    6 комментариев
  • Как, имея строку с ключами, получить массив значений из вложенных объектов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const result = Array.prototype.reduce.call(letter, (acc, n) => (
      (n = soundParts[Number.isNaN(+n) ? 'letter' : 'number'][n]) && acc.push(n),
      acc
    ), []);

    или

    const result = Object
      .entries(Object.assign({}, ...Object.values(soundParts)))
      .reduce((acc, n) => (letter.includes(n[0]) && acc.push(n[1]), acc), []);
    Ответ написан
  • Как получить подстроку в данном случае?

    0xD34F
    @0xD34F Куратор тега JavaScript
    str.match(/\]\s*(.+?)\s*#/)?.[1] ?? '< unknown >'
    Ответ написан
    Комментировать
  • Как сгруппировать расположенные последовательно элементы массива с одинаковыми значениями одного из свойств?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const groupAdjacent = (arr, newGroup) =>
      arr.reduce((acc, n, i, a) => (
        (!i || newGroup(n, a[~-i])) && acc.push([]),
        acc.at(-1).push(n),
        acc
      ), []);

    const result = groupAdjacent(arr, (c, p) => c.name !== p.name);
    Ответ написан
  • Почему в корзине считается только последний discount?

    0xD34F
    @0xD34F Куратор тега JavaScript
    for (let i in goods) {
      if (discounts.length === 0) {
        count = goods[i].value * goods[i].amount;
      }
      for (let j in discounts) {
        if (goods[i].name === discounts[j].name) {
          count =
            (goods[i].value - goods[i].value * discounts[j].discount) *
            goods[i].amount;
        } else {
          count = goods[i].value * goods[i].amount;
        }
      }
      sum += count;
      console.log(count, goods[i].name);
    }

    Зачем на каждой итерации цикла, ищущего скидку, пересчитывать цену? Предположим, нашли скидку для текущего товара, что дальше? Дальше, на следующей итерации, цена будет рассчитана заново, но уже без скидки - ведь объект скидки будет другим, будет соответствовать другому товару. Исключение - если скидка найдена на последней итерации.

    Правильно будет вычислять до цикла сумму без скидки, искать в цикле скидку, если нашли - пересчитывать со скидкой, прерывать цикл:

    for (const n of goods) {
      let s = n.value * n.amount;
      for (const m of discounts) {
        if (n.name === m.name) {
          s *= 1 - m.discount;
          break;
        }
      }
      sum += s;
    }

    Возможно есть лучший способ для решения такой задачи.

    function totalCost(goods, discounts) {
      discounts = Object.fromEntries(discounts.map(n => [ n.name, 1 - parseFloat(n.discount) / 100 ]));
      return goods.reduce((acc, n) => acc + n.value * n.amount * (discounts[n.name] ?? 1), 0);
    }
    Ответ написан
    3 комментария
  • Как узнать индекс гласных в слове?

    0xD34F
    @0xD34F Куратор тега JavaScript
    let letters = 'АаЕеIiOoUuYy'  //Создал переменную со строкой с гласными

    Почему символы из разных алфавитов? - первые четыре кириллические.

    if (arg1[i] == letters[j]) {  //Если элемент слова равен элементу строки с согласными
      newArr.push(arg1[i].indexOf())  //То пушу в массив индексы гласных этого слова
    }

    Зачем тут indexOf (кстати, вы не знаете, что он делает, откройте документацию и разберитесь)? Что является индексом проверяемого символа? Строчкой выше ещё помнили, а когда до push'а дело дошло, уже забыли? Просто .push(i).

    Не ошибка, но упомянуть косячок стоит - после найденного совпадения продолжать крутить внутренний цикл смысла нет. Гуглите, как прервать цикл.

    Ну и это, как ещё можно сделать:

    const getVowelsIndexes = str =>
      Array.from(str.matchAll(/[aeiouy]/gi), n => n.index);

    или

    const getVowelsIndexes = ((vowels, str) =>
      Array.prototype.reduce.call(
        str.toLowerCase(),
        (acc, n, i) => (vowels.has(n) && acc.push(i), acc),
        []
      )
    ).bind(null, new Set('aeiouy'));

    или

    const getVowelsIndexes = str => Object
      .entries(str)
      .filter(n => 'AaEeIiOoUuYy'.includes(n[1]))
      .map(n => +n[0]);

    или

    const getVowelsIndexes = str =>
      eval(`[${[...str]
        .map((n, i) => /a|e|i|o|u|y/i.test(n) ? `${i},` : '')
        .join('')
      }]`);
    Ответ написан
    Комментировать
  • Как изменить цвет ссылки при наличии в ней определённого текста?

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

    const className = 'some-link';
    const colors = {
      En:   'red',
      Ru: 'green',
      De:  'blue',
    };

    Перекрашиваем текст в случае точного совпадения:

    for (const n of document.getElementsByClassName(className)) {
      n.style.setProperty('color', colors[n.textContent]);
    }

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

    document.querySelectorAll(`.${className}`).forEach(function(n) {
      n.style.color = this.find(m => m[0].test(n.innerText))?.[1];
    }, Object.entries(colors).map(n => [ RegExp(n[0], 'i'), n[1] ]));

    Можно и без регулярных выражений:

    - RegExp(n[0], 'i')
    + n[0].toLowerCase()

    - m[0].test(n.innerText)
    + n.innerText.toLowerCase().includes(m[0])
    Ответ написан
    Комментировать
  • Как написать этот код легче?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const newData = data.reduce((acc, n) => {
      const k = Object.keys(n)[0];
      (acc.result[acc.keys[k] = (acc.keys[k] ?? -1) + 1] ??= []).push(n);
      return acc;
    }, { result: [], keys: {} }).result.flat();

    или

    const numKeys = new Set(data.flatMap(Object.keys)).size;
    const numObjs = data.length / numKeys;
    const newData = data.map((n, i, a) => a[(i % numKeys) * numObjs + (i / numKeys | 0)]);
    Ответ написан
    2 комментария
  • Если в диве больше 1го элемента то добавить класс к диву?

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

    const selector = 'селектор элементов';
    const className = 'класс';
    const minChildrenCount = 666;

    Добавляем:

    for (const n of document.querySelectorAll(selector)) {
      n.classList.toggle(className, n.children.length >= minChildrenCount);
    }
    
    // или (нет, так делать точно не надо - для 0 результат будет некорректным)
    
    document
      .querySelectorAll(`${selector} > :nth-child(${minChildrenCount})`)
      .forEach(n => n.parentNode.classList.add(className));
    Ответ написан
    Комментировать
  • Одномерный массив в многомерный?

    0xD34F
    @0xD34F Куратор тега JavaScript
    (function toArrays(obj) {
      const arr = Object.values(obj ?? {});
      arr.forEach(n => n.children = toArrays(n.children));
      return arr;
    })(arr.reduce((acc, n) => {
      const path = n.path.replace(/^\/|\/$/g, '').split('/');
      const obj = path.reduce((p, c) => ((p.children ??= {})[c] ??= {}), acc);
      Object.assign(obj, n);
      return acc;
    }, {}).children)
    Ответ написан
    2 комментария
  • Как переписать функцию со scrollIntoView?

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

    <div class="mainMenu">
      <button data-scroll-to="calendar">Раз</button>
      <button data-scroll-to="rooms">Два</button>
      <button data-scroll-to="maps">Три</button>
      <button data-scroll-to="contact">Четыре</button>
    </div>
    ...
    <div data-block="calendar">...</div>
    <div data-block="rooms">...</div>
    <div data-block="maps">...</div>
    <div data-block="contact">...</div>

    Сделать функцию, которая получает значение атрибута, находит соответствующий блок и выполняет прокрутку:

    function scrollTo(block) {
      document.querySelector(`[data-block="${block}"]`).scrollIntoView({
        block: 'center',
        behavior: 'smooth',
      });
    }

    Воспользоваться этой функцией при обработке кликов по кнопкам:

    document.querySelector('.mainMenu').addEventListener('click', e => {
      const block = e.target.dataset.scrollTo;
      if (block) {
        scrollTo(block);
      }
    });
    
    // или
    
    document.querySelectorAll('[data-scroll-to]').forEach(function(n) {
      n.addEventListener('click', this);
    }, e => scrollTo(e.target.dataset.scrollTo));
    Ответ написан
    1 комментарий
  • Как сделать алгоритм добавления елементов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    new Set(strings).forEach(function(n) {
      this.has(n) || objects.push({ name: n });
    }, new Set(objects.map(n => n.name)));

    или

    for (const name of strings) {
      if (objects.every(n => n.name !== name)) {
        objects[objects.length] = { name };
      }
    }

    или

    objects.splice(0, objects.length, ...strings.reduce(
      (acc, n) => acc.set(n, acc.get(n) ?? { name: n }),
      new Map(objects.map(n => [ n.name, n ]))
    ).values());
    Ответ написан
    Комментировать
  • Как в списке удалить весь текст выше 3-рёх подряд строк заканчивающихся на знаки препинания в конце?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const punct = '.,!?;:"\'”“';
    const numStrWithPunctEnd = 3;
    const arr = str.split('\n');
    const index = arr.findIndex(function(n, i, a) {
      return this.every(m => punct.includes(a[i + m]?.slice(-1)));
    }, [...Array(numStrWithPunctEnd).keys()]);
    const result = index !== -1 ? arr.slice(index).join('\n') : str;
    Ответ написан
  • Почему неправильно парсится дата?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Число перепутано с днём недели; не добавлена единица номеру месяца (они с нуля начинаются).

    Ну и сам формат не везде поддерживается - в фаерфоксе получите Invalid Date. Выдирайте из строки компоненты даты и засовывайте их в конструктор по отдельности, или воспользуйтесь какой-нибудь готовой библиотекой для работы с датами.
    Ответ написан
  • Как отслеживать смену значений в объекте?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Заворачиваете свой объект в Proxy:

    const obj = {
      a: 69,
      b: 187,
      c: 666,
    };
    
    const proxy = new Proxy(obj, {
      set(target, key, val) {
        console.log('свойство', key, 'изменило своё значение с', target[key], 'на', val);
        target[key] = val;
        return true;
      },
    });

    Дальше вместо исходного объекта работаете с экземпляром Proxy.
    Ответ написан
    2 комментария