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

    0xD34F
    @0xD34F Куратор тега JavaScript
    $(document).on('click', 'a[id^="answer_"]', e => $(`#comment_${e.target.id}`).toggle());
    Ответ написан
    1 комментарий
  • Как изменить значение в объекте по его пути?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function setNestedVal(root, path, val) {
      const keys = path.split('.');
      const key = keys.pop();
      keys.reduce((acc, n) => acc[n] ??= {}, root)[key] = val;
    }

    setNestedVal(data, '0.arr.0.str', 'hello, world!!');
    setNestedVal(data, '0.arr.1.num', 666);

    Интересуют готовые решения

    https://lodash.com/docs/4.17.15#set

    А вообще, учитывая что за задачу вы пытаетесь решить...

    Нужно изменить этот массив, преобразовав все строки с датами в нём, независимо от того, как глубоко они вложены, в объекты.

    ...вам ничего из того, что выше, не нужно. Делаем функцию, которая рекурсивно обойдёт переданный объект, проверяя, чем являются значения - если строкой с датой, то выполняет указанное преобразование:

    const replaceValues = (data, test, transform) =>
      test(data)
        ? transform(data)
        : data instanceof Array
          ? data.map(n => replaceValues(n, test, transform))
          : data instanceof Object
            ? Object.fromEntries(Object
                .entries(data)
                .map(([ k, v ]) => [ k, replaceValues(v, test, transform) ])
              )
            : data;

    const newData = replaceValues(
      data,
      x => typeof x === 'string' && /^\d{2}\.\d{2}\.\d{4}$/.test(x),
      str => new Date(str.split('.').reverse().join('-'))
    );
    Ответ написан
    Комментировать
  • Как посчитать проценты?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const valueKey = 'number';
    const percentKey = 'percent';

    Что такое процент - отношение конкретного значения и суммы всех значений, умноженное на сто:

    const sum = arr.reduce((acc, n) => acc + n[valueKey], 0);
    const getPercent = n => n[valueKey] / sum * 100;

    Собираем новый массив или обновляем существующий:

    const newArr = arr.map(n => ({ ...n, [percentKey]: getPercent(n) }));
    
    // или
    
    arr.forEach(n => n[percentKey] = getPercent(n));
    Ответ написан
    1 комментарий
  • Как склеить значения одного ключа?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Object
      .entries(arr.reduce((acc, n) => (
        n = n.match(/('.*?'):'(.*?)'/),
        (acc[n[1]] ??= []).push(n[2]),
        acc
      ), {}))
      .map(n => `${n[0]}:'${n[1].join('|')}'`)
    Ответ написан
    3 комментария
  • Regex: как убрать все пробелы только между словами?

    0xD34F
    @0xD34F Куратор тега JavaScript
    str.replace(/(?<=\S)\s+(?=\S)/g, '')
    
    // или
    
    str.split(/(?<=\S)\s+(?=\S)/).join``
    
    // или
    
    str.match(/(^\s+)|\S+|(\s+$)/g)?.join('') ?? ''
    Ответ написан
    Комментировать
  • Как в alpine js удалить элемент?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Очевидно, внести соответствующие изменения в данные, связанные с отображением элемента.

    Например.
    <script defer src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.3.4/cdn.min.js"></script>
    
    <div x-data="{
      items: [
        { id:  69, text:  'hello, world!!' },
        { id: 187, text:  'fuck the world' },
        { id: 666, text: 'fuck everything' },
      ],
    }">
      <template x-for="(n, i) in items">
        <div>
          <span x-text="n.text"></span>
          <button @click="items.splice(i, 1)">x</button>
        </div>
      </template>
    </div>

    https://jsfiddle.net/96ksgtdo/
    Ответ написан
    Комментировать
  • Как написать итератор для вывода нечетных чисел используя [Symbol.iterator]?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Код, который работает некорректно...

    Всё работает так, как и должно. После Object.freeze назначить новое свойство уже не получится. Можно записать итератор в прототип массива (ну и после того, как он отработает, вернуть оригинальный итератор на место):

    const originalIterator = Array.prototype[Symbol.iterator];
    
    Array.prototype[Symbol.iterator] = function() {
      let i = -1;
    
      return {
        next: () => (i += 2) < arr.length
          ? { done: false, value: this[i] }
          : { done: true },
      };
    };
    
    setTimeout(() => Array.prototype[Symbol.iterator] = originalIterator, 0);
    Ответ написан
    4 комментария
  • Как из массива массивов сделать один массив?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const result = arr.flat();
    
    // или
    
    const result = Array.prototype.concat.apply([], arr);
    
    // или
    
    const result = arr.reduce((acc, n) => (acc.push(...n), acc), []);
    
    // или
    
    const result = [];
    for (const n of arr) {
      for (const m of n) {
        result[result.length] = m;
      }
    }
    Ответ написан
    1 комментарий
  • Как скрыть родителя, если у детей определённое значение?

    0xD34F
    @0xD34F Куратор тега JavaScript
    О каких элементах идёт речь:

    const outerClass = 'filter__item';
    const innerClass = 'filter__checkgroup-count';

    Скрываем:

    OUTER:
    for (const n of document.getElementsByClassName(outerClass)) {
      for (const m of n.getElementsByClassName(innerClass)) {
        if (Number(m.innerText)) {
          continue OUTER;
        }
      }
    
      n.hidden = true;
      // или
      n.style.display = 'none';
      // или
      n.style.setProperty('visibility', 'hidden');
      // или
      n.style.cssText += 'opacity: 0';
      // или
      n.setAttribute('style', 'transform: scale(0)');
    }

    или

    .hidden {
      display: none;
    }

    document.querySelectorAll(`.${outerClass}`).forEach(n => {
      n.classList.toggle('hidden', !Array.prototype.some.call(
        n.querySelectorAll(`.${innerClass}`),
        m => +m.textContent
      ));
    });
    Ответ написан
    1 комментарий
  • Как оставить уникальные объекты из двух массивов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function isEqual(a, b) {
      const keysA = Object.keys(a);
      const keysB = Object.keys(b);
      return keysA.length === keysB.length && keysA.every(n => a[n] === b[n]);
    }
    
    
    const result = arr1.concat(arr2.filter(n => arr1.every(m => !isEqual(n, m))));

    или

    const unique = function(arr, keys = n => n) {
      const picked = new Map;
      return arr.filter((...args) => {
        const p = []
          .concat(keys(...args))
          .reduce((acc, k) => acc.set(k, acc.get(k) ?? new Map).get(k), picked);
    
        return !p.set(this, p.has(this)).get(this);
      });
    }.bind(Symbol());
    
    
    const result = unique([ ...arr1, ...arr2 ], n => [ n.teamId, n.userId ]);
    Ответ написан
    Комментировать
  • Как получить массив массивов с 2-мя объектами из 2-ух массивов с объектами?

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

    1. Если уверены, что длины всех массивов будут равны:

      const zip = (...arrs) =>
        arrs[0]?.map((n, i) => arrs.map(m => m[i])) ?? [];
      
      
      const result = zip(arr1, arr2);


    2. Вместо отсутствующих значений подставляем какое-то дефолтное:

      const zip = (arrs, defaultValue = null) =>
        Array.from(
          { length: Math.max(...arrs.map(n => n.length)) },
          (n, i) => arrs.map(m => i < m.length ? m[i] : defaultValue)
        );
      
      // или
      
      const zip = (arrs, defaultValue = null) =>
        arrs.reduce((acc, n, i) => (
          n.forEach((m, j) => (acc[j] ??= Array(arrs.length).fill(defaultValue))[i] = m),
          acc
        ), []);

      В вашем случае применять, понятное дело, так: const result = zip([ arr1, arr2 ]);.

      Пример с разными длинами массивов - zip([ [ 1, 2, 3 ], [], [ 99 ] ], Infinity) - в результате получим

      [
        [ 1, Infinity,       99 ],
        [ 2, Infinity, Infinity ],
        [ 3, Infinity, Infinity ]
      ]


    3. В качестве источников данных могут выступать не обязательно массивы:

      function* zip(data, defaultValue = null) {
        const iterators = Array.from(data, n => n[Symbol.iterator]());
      
        for (let doneAll = false; doneAll = !doneAll;) {
          const values = [];
      
          for (const n of iterators) {
            const { value, done } = n.next();
            values.push(done ? defaultValue : value);
            doneAll &&= done;
          }
      
          if (!doneAll) {
            yield values;
          }
        }
      }

      В вашем случае применять так: const result = [...zip([ arr1, arr2 ])];.

      Пример посложнее, результатом выражения

      Array.from(zip((function*() {
        yield [ , true, false, NaN ];
        yield 'abcde';
        yield Array(3).keys();
      })()))

      будет следующий массив:

      [
        [ undefined, 'a',    0 ],
        [      true, 'b',    1 ],
        [     false, 'c',    2 ],
        [       NaN, 'd', null ],
        [      null, 'e', null ]
      ]


    Ответ написан
    1 комментарий
  • Как разбить массив на части заданного размера?

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

    const chunked = (arr, chunkSize) =>
      arr.reduce((acc, n, i) => (
        (i % chunkSize) || acc.push([]),
        acc.at(-1).push(n),
        acc
      ), []);
    
    
    console.log(chunked([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], 3));

    А можно - как частный случай группировки, и не обязательно одних только массивов:

    const group = (data, key) =>
      Array.prototype.reduce.call(
        data,
        (acc, n, i, a) => ((acc[key(n, i, a)] ??= []).push(n), acc),
        {}
      );
    
    const chunked = (data, chunkSize) =>
      Object.values(group(data, (_, i) => i / chunkSize | 0));
    
    
    console.log(chunked('0123456789', 3));
    console.log(chunked(document.querySelectorAll('img'), 5));

    Если режем на куски не массив, то и кусками тоже могут быть не массивы, а значения исходного типа (если данный тип поддерживает операцию получения куска значения):

    const chunked = (data, chunkSize, slice = data.slice) =>
      Array.from(
        { length: Math.ceil(data.length / chunkSize) },
        function(_, i) {
          return this(i * chunkSize, (i + 1) * chunkSize);
        },
        (slice instanceof Function ? slice : Array.prototype.slice).bind(data)
      );
    
    
    console.log(chunked('abcdefghij', 4)); // так кусками будут тоже строки
    console.log(chunked('abcdefghij', 4, [].slice)); // а так - массивы
    console.log(chunked($('img'), 5));

    В качестве исходных данных могут выступать не только массивоподобные объекты, но и итерируемые, а саму выдачу кусков можно оформить в виде генератора:

    function* chunked(data, chunkSize) {
      let chunk = [];
    
      for (const n of data) {
        if (chunk.push(n) === chunkSize) {
          yield chunk;
          chunk = [];
        }
      }
    
      if (chunk.length) {
        yield chunk;
      }
    }
    
    
    console.log(Array.from(chunked([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], 3)));
    console.log([...chunked(document.querySelectorAll('img'), 5)]);
    for (const n of chunked(Array(10).keys(), 4)) {
      console.log(n);
    }
    Ответ написан
    Комментировать
  • Как разбить массив на подмассивы по дате?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Object.values(arr.reduce((acc, { date, ...n }) => (
      (acc[date] ??= { date, name_type: [] }).name_type.push(n),
      acc
    ), {}))

    Можно пример с lodash.

    _.map(_.groupBy(arr, 'date'), (v, k) => ({
      date: k,
      name_type: _.map(v, n => _.omit(n, 'date')),
    }))
    Ответ написан
    Комментировать
  • Как в массиве найти элемент другого массива?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Ищем индекс одного (первого встреченного) элемента, присутствующего в обоих массивах:

    const index = arr1.findIndex(n => arr2.indexOf(n) !== -1);

    Получаем индексы всех таких элементов:

    // просто массив индексов
    const indices = arr1.reduce((acc, n, i) => (arr2.includes(n) && acc.push(i), acc), []);
    
    
    // объект вида { элемент: индекс первого вхождения }
    const indices = arr2.reduce((acc, n) => (
      acc[0].hasOwnProperty(n) && (acc[1][n] = acc[0][n]),
      acc
    ), [ arr1.reduce((acc, n, i) => (acc[n] ??= i, acc), {}), {} ])[1];
    
    
    // Map вида { элемент: массив всех индексов данного элемента }
    const indices = arr1.reduce((acc, n, i) => (
      acc[0].has(n) && acc[1].set(n, acc[1].get(n) ?? []).get(n).push(i),
      acc
    ), [ new Set(arr2), new Map ])[1];
    Ответ написан
    1 комментарий
  • Как в массиве чисел найти ближайшее к заданному?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const findClosest = (arr, num) =>
      arr.reduce((closest, n) =>
        Math.abs(n - num) > Math.abs(closest - num)
          ? closest
          : n
      , undefined);
    
    
    const arr = [ 110, 200, 390, 488, 509 ];
    const num = 220;
    
    const closest = findClosest(arr, num);
    Ответ написан
    Комментировать
  • Как реализовать задачу с заполнением очереди запросов на сервер?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const runTasks = (tasks, max = 1) =>
      new Promise(resolve => {
        const results = Array(tasks.length).fill(null);
        let started = 0;
        let finished = 0;
    
        for (let i = Math.max(1, Math.min(max, tasks.length)); i--; run()) ;
    
        async function run() {
          if (finished === tasks.length) {
            resolve(results);
          } else if (started < tasks.length) {
            const index = started++;
    
            try {
              results[index] = await tasks[index]();
            } catch (e) {
              results[index] = e;
            }
    
            finished++;
            run();
          }
        }
      });
    
    const sendRequests = (urls, ...args) =>
      runTasks(urls.map(n => () => fetch(n).then(r => r.json())), ...args);
    Ответ написан
    Комментировать
  • Как составить регулярное выражение, чтобы получить форматированную строку в JavaScript?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const printf = (str, ...params) =>
      str.replace(/\$(\d+)/g, (m, g1) => params[~-g1] ?? m);
    Ответ написан
    1 комментарий
  • Как сгруппировать и отсортировать массив имён?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const groupedAndSortedArr = Object
      .values(arr.reduce((acc, n) => ((acc[n[0]] ??= []).push(n), acc), {}))
      .sort((a, b) => a[0][0].localeCompare(b[0][0]));
    Ответ написан
    6 комментариев
  • Какое регулярное выражение использовать для получения части строки?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Не надо никаких регулярных выражений:

    str.split('=').pop()
    // или
    str.slice(str.indexOf('=') + 1)

    Но, конечно, можно и регулярками:

    str.replace(/[^=]*=/, '')
    // или
    str.match(/(?<==).*/)[0]
    // или
    /[^=]*$/.exec(str).shift()

    А вообще, учитывая, чем является эта строка, и что вы хотите из неё получить:

    new URLSearchParams(str).get('sort')
    Ответ написан
    1 комментарий
  • Как остановить выбор текста на первой кавычке?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Ответ написан
    Комментировать