• Как сгруппировать данные списка?

    0xD34F
    @0xD34F
    grouped = {}
    
    for n in products:
      category = n['category']['name']
      group = grouped.setdefault(category, {})
      group[n['productId']] = { 'name': n['name'] }
    Ответ написан
    1 комментарий
  • Как получить самую давнюю дату, хранящуюся в массиве?

    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
    Собрать новый массив:

    $newArr = array_filter($arr, fn($n) => explode('.', $n)[1] !== '08');

    Изменить существующий:

    for ($i = 0, $j = 0; $i < count($arr); $i++) {
      if (explode('.', $arr[$i])[1] === '08') {
        $j++;
      } else if ($j) {
        $arr[$i - $j] = $arr[$i];
      }
    }
    
    array_splice($arr, -$j, $j);
    Ответ написан
    2 комментария
  • Как сделать кнопку больше-меньше на React чтобы скрыть или показать li?

    0xD34F
    @0xD34F Куратор тега React
    const [ showAll, setShowAll ] = useState(false);
    const defaultShow = 2;
    const showAllByDefault = ingredients.length <= defaultShow;
    const ingredientsToShow = (showAll || showAllByDefault)
      ? ingredients
      : ingredients.slice(0, defaultShow);

    {showAllByDefault
      ? null
      : <button onClick={() => setShowAll(val => !val)}>{showAll ? 'hide' : 'show'}</button>
    }
    <ul>
      {ingredientsToShow.map(n => <li>{n}</li>)}
    </ul>
    Ответ написан
    1 комментарий
  • Как запустить computed в VUE3?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Ключевое слово в качестве имени переменной - что за бред?
    Как достучаться до параметров из script setup - читаем документацию.
    У вычисляемого свойства нет сеттера - так пусть будет функцией, объект не нужен.

    Исправляем:

    const props = defineProps({
      showBgBlue: {
        type: Boolean,
        default: true,
      },
    });
    
    const mainClass = computed(() => props.showBgBlue ? 'bg-blueGray-50 py-12' : 'bg-white');
    Ответ написан
    5 комментариев
  • Как найти сумму элементов вложенных массивов?

    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.
    Ответ написан
  • Как можно улучшить модерирование?

    0xD34F
    @0xD34F
    Через форму обратной связи изложить администрации свои гениальные идеи (нет, это не насмешка, не сарказм - там такой пункт есть в выпадающем списке "тема"). Дальше надеяться, что администрация вас услышит, согласиться с услышанным и укажет модераторам на необходимость смены поведенческих паттернов.
    Ответ написан
    2 комментария
  • Почему при обновлении блока на нем перестает работать mouseover?

    0xD34F
    @0xD34F Куратор тега Vue.js
    new Vue({

    loaves: [

    this.loaves[idx] =

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

    Перезапись элемента массива не отслежена => его свойства не стали реактивными => их изменения не приводят к обновлению 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;
    Ответ написан
    Комментировать
  • Как реализовать анимацию набора текста?

    0xD34F
    @0xD34F Куратор тега React
    function Typewriter({ text }) {
      const [ length, setLength ] = useState(0);
    
      useEffect(() => {
        setLength(0);
    
        const interval = setInterval(setLength, 100, length => {
          if (++length >= text.length) {
            clearInterval(interval);
          }
    
          return length;
        });
    
        return () => clearInterval(interval);
      }, [ text ]);
    
      return <div>{text.slice(0, length)}</div>;
    }
    Ответ написан
    Комментировать
  • Как через цикл вложить элементы 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 комментария
  • Как заранее знать уровень подготовки спрашивающего?

    0xD34F
    @0xD34F
    Считайте спрашивающего конченым дебилом. В девяти случаях из десяти окажетесь правы. Может даже в девятнадцати из двадцати. Нет, серьёзно, кем ещё надо быть, чтобы спрашивать то, что можно загуглить?
    Ответ написан
  • Как сделать сортировку 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 комментария
  • Как поменять действие по нажатию на label dataset'а?

    0xD34F
    @0xD34F
    В документации не нашёл явных примеров.

    плохо искали

    options: {
      plugins: {
        legend: {
          onClick(e, legendItem, { chart }) {
            this.legendItems.forEach((n, i) => {
              chart.getDatasetMeta(i).hidden = n !== legendItem && (legendItem.hidden || !n.hidden);
            });
    
            chart.update();
          },
        },
      },
    },

    https://jsfiddle.net/79Lfjor6/
    Ответ написан
    Комментировать
  • Как заставить работать кастомную навигацию Swiper в React?

    0xD34F
    @0xD34F Куратор тега React
    nextEl: prevRef.current

    Суть проблемы - current имеет значение null при первом рендеринге. Так что придётся заставить компонент отрендериться ещё раз. Например, можно это сделать путём сохранения ссылки на экземпляр swiper'а в стейт (да, с одной стороны костыль, с другой - наверняка пригодится в будущем):

    const [ swiper, setSwiper ] = useState();

    <Swiper
      onSwiper={setSwiper}
      ...

    https://codesandbox.io/s/for-https-qna-habr-com-q-...

    Или, так же получаете экземпляр swiper'а, но вырезаете к чёрту navigation, вместо того, чтобы передавать ref'ы в экземпляр компонента Swiper, добавьте элементам, которые должны выступать в роли кнопок, обработчики клика, где будут дёргаться методы slidePrev/slideNext:

    - ref={prevRef}
    + onClick={() => swiper.slidePrev()}

    https://stackblitz.com/edit/stackblitz-starters-rr...
    Ответ написан
  • Как обрезать слишком длинный текст при выводе?

    0xD34F
    @0xD34F Куратор тега Vue.js
    data: () => ({
      maxlen: 10,
      items: [
        'hello, world!!',
        'fuck the world',
        'fuck everything',
        '1234567890',
        'Lorem ipsum dolor sit amet, consectetur adipiscing elit,',
       ],
    }),
    methods: {
      short1: (str, maxlen) => str.length <= maxlen ? str : str.slice(0, maxlen) + '...',
      short2: (str, maxlen) => str.substring(0, maxlen) + (str.charAt(maxlen) && '...'),
      short3: (str, maxlen) => str.replace(RegExp(`(.{${maxlen}}).+`), '$1...'),
      short4: (str, maxlen) => str.replace(RegExp(`(?<=.{${maxlen}}).+`), '...'),
    },

    <input type="range" min="1" max="20" v-model="maxlen">
    <span>{{ maxlen }}</span>
    
    <div v-for="n in items">
      <div v-text="short1(n, maxlen)"></div>
      <div v-html="short2(n, maxlen)"></div>
      <div :text-content.prop="short3(n, maxlen)"></div>
      <div>{{ short4(n, maxlen) }}</div>
    </div>

    https://jsfiddle.net/dxm0gsnf/
    Ответ написан
    Комментировать
  • Как выполнять вычисления с помощью функций (результат вызова предыдущей должен быть аргументом следующей)?

    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 комментарий