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

    0xD34F
    @0xD34F Куратор тега JavaScript
    Не надо ничего отнимать, что-то изменилось - посчитали всё с нуля:

    const questionEl = document.querySelector('ul');
    const resultsEl = document.querySelector('p span');
    
    questionEl.addEventListener('change', showResults);
    
    function showResults() {
      resultsEl.innerText = Array.prototype.reduce.call(
        questionEl.querySelectorAll('input[type="radio"]:checked'),
        (acc, n) => acc + +n.value,
        0
      );
    }

    А вообще, правильно было бы показывать результат только после получения всех ответов; вопросы показывать по одному; не зашивать в разметку вопросы и варианты ответов. Как-то так.
    Ответ написан
    Комментировать
  • Как извлечь из вложенной структуры элементы удовлетворяющие условию?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Как будем проверять, что значение нам подходит:

    const isValid = n => n.workControl?.includes('intermediate');

    Рекурсия есть:

    const getFromTree = (tree, childrenKey, test) =>
      Array.isArray(tree)
        ? tree.reduce((acc, n) => (
            test(n) && acc.push(n),
            acc.push(...getFromTree(n[childrenKey], childrenKey, test)),
            acc
          ), [])
        : [];
    
    const result = getFromTree(tree, 'content', isValid);

    или

    function* getNestedData(data, test) {
      if (test(data)) {
        yield data;
      }
    
      if (data instanceof Object) {
        for (const k in data) if (Object.hasOwn(data, k)) {
          yield* getNestedData(data[k], test);
        }
      }
    }
    
    const result = [...getNestedData(tree, isValid)];

    Рекурсии нет:

    function getNestedData(data, test) {
      const result = [];
    
      for (const stack = [ data ]; stack.length;) {
        const n = stack.pop();
    
        if (test(n)) {
          result.push(n);
        }
    
        if (n instanceof Object) {
          stack.push(...Object.values(n).reverse());
        }
      }
    
      return result;
    }

    или

    const getFromTree = function*(tree, childrenKey, test) {
      const stack = [];
    
      for (let [ i, arr ] = this(tree); ++i < arr.length || stack.length;) {
        if (i === arr.length) {
          [ i, arr ] = stack.pop();
        } else {
          if (test(arr[i])) {
            yield arr[i];
          }
    
          stack.push([ i, arr ]);
          [ i, arr ] = this(arr[i][childrenKey]);
        }
      }
    }.bind(x => [ -1, x instanceof Array ? x : [] ]);
    Ответ написан
    5 комментариев
  • Как добавить несколько наименований в одну метку?

    0xD34F
    @0xD34F Куратор тега Яндекс.Карты
    Сгруппируйте данные по координатам:

    const grouped = productData.reduce((acc, n) => (
      (acc[n.coordinates] ??= []).push(n),
      acc
    ), {});

    Соответственно, когда будете собирать строку для ballonContent метки, вместо одного объекта придётся пробежать по массиву объектов:

    for (const [ coord, data ] of Object.entries(grouped)) {
      const placemark = new ymaps.Placemark(
        coord.split(',').map(parseFloat),
        {
          balloonContent: data
            .map(n => `
              <div>
                ${n.address}
                <br>
                <a href="${n.productURL}">Подробнее</a>
              </div>`)
            .join(''),
        },
        {
          preset: 'islands#blueDotIcon',
          maxWidth: 300,
        }
      );
    
      map.geoObjects.add(placemark);
    }

    Или, воспользуйтесь кластеризатором:

    const placemarks = productData.map((n, i) => new ymaps.Placemark(
      n.coordinates.split(',').map(parseFloat),
      {
        balloonContent: `${n.address}<br><a href="${n.productURL}">Подробнее</a>`,
        clusterCaption: `Адрес №${i + 1}`,
      },
      {
        preset: 'islands#blueDotIcon',
        maxWidth: 300,
      }
    ));
    
    const clusterer = new ymaps.Clusterer({
      clusterDisableClickZoom: true,
    });
    
    clusterer.add(placemarks);
    map.geoObjects.add(clusterer);
    Ответ написан
    1 комментарий
  • Как выполнить несколько замен в строке так, следующие замены не перетирали результат предыдущих?

    0xD34F
    @0xD34F
    Вместо того, чтобы перебирать "алфавит" и заменять символы по одному, перебирайте "зашифрованный" текст и подставляйте вместо текущего символа соответствующий ему из "алфавита":

    decoded = ''.join(data_crypt.get(n, n) for n in text)
    print(decoded)
    Ответ написан
    3 комментария
  • Как сделать, чтобы количество активных чекбоксов не опускалось ниже определённого?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Где чекбоксы находятся, что за чекбоксы, каковы ограничения на количество отмеченных, как посчитать количество отмеченных:

    const container = document.querySelector('селектор общего предка чекбоксов');
    const checkboxSelector = 'селектор чекбоксов';
    const minChecked = 1;
    const maxChecked = Infinity;
    const countChecked = checkboxes =>
      Array.prototype.reduce.call(checkboxes, (acc, n) => acc + n.checked, 0);

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

    const checkboxes = container.querySelectorAll(checkboxSelector);
    const onChange = () => {
      const count = countChecked(checkboxes);
      const minReached = count <= minChecked;
      const maxReached = count >= maxChecked;
      checkboxes.forEach(n => n.disabled = minReached && n.checked || maxReached && !n.checked);
    };
    
    checkboxes.forEach(n => n.addEventListener('change', onChange));

    Или, выставляйте снятый чекбокс обратно, если количество отмеченных упало ниже минимума и снимайте выставленный, если количество отмеченных превысило максимум:

    container.addEventListener('change', function({ target: t }) {
      if (t.matches(checkboxSelector)) {
        const count = countChecked(this.querySelectorAll(checkboxSelector));
        t.checked ||= count < minChecked;
        t.checked &&= count <= maxChecked;
      }
    });
    Ответ написан
    3 комментария
  • Почему итератор нужно делать итерируемым?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Посмотрите на результат выполнения следующего кода с [Symbol.iterator]() { return this; } и без:

    const iter = new Range(1, 5)[Symbol.iterator]();
    console.log(iter.next().value);
    console.log(iter.next().value);
    console.log([...iter]);
    Ответ написан
    2 комментария
  • Как получить индекс элемента с определенным классом на jQuery?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Пытаюсь так:

    let currentIndex = $(".js-practice_button.current").index();

    Но значение всегда 0, у какой бы кнопки класс current не присутствовал.

    Потому что метод index по умолчанию определяет индекс элемента среди соседей, а так как у каждой кнопки есть отдельный родитель... Ну да, получаете то, что получаете.

    Можно вместо индекса кнопки определять индекс родителя:

    const index = $('.js-practice_button.current').closest('li').index();

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

    const index = $('.js-practice_button.current').index('.js-practice_button');

    А вообще, к чёрту jquery. Есть варианты и на чистом js:

    const container = document.querySelector('.js-practices_buttons');
    const itemSelector = '.practice_item';
    const buttonSelector = `${itemSelector} .js-practice_button`;
    const activeClass = 'current';
    const activeSelector = `.${activeClass}`;

    const index = Array.prototype.findIndex.call(
      container.querySelectorAll(buttonSelector),
      n => n.classList.contains(activeClass)
    );
    
    // или
    
    const { children } = container;
    let index = children.length;
    while (index-- && !children[index].matches(`:has(${activeSelector})`)) ;
    
    // или
    
    const index =
      (el => el ? [...el.parentNode.children].indexOf(el) : -1)
      (container.querySelector(`${itemSelector}:has(${activeSelector})`));
    
    // или
    
    let index = -1;
    for (
      let el = container.querySelector(activeSelector)?.closest(itemSelector);
      el;
      el = el.previousElementSibling, index++
    ) ;
    Ответ написан
    Комментировать
  • Как удалить из массива числа с повторяющимися цифрами?

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

    const noRepeatingDigits = num => !/(\d).*\1/.test(num);
    
    // или
    
    const noRepeatingDigits = num => -~Math.log10(num) === new Set(`${num}`).size;
    
    // или
    
    const noRepeatingDigits = num => [...'' + num]
      .filter((n, i, a) => i !== a.indexOf(n))
      .length === 0;
    
    // или
    
    const noRepeatingDigits = num => String(num)
      .split('')
      .reduce((acc, n) => (acc[n]++, acc), Array(10).fill(0))
      .every(n => n < 2);
    
    // или
    
    const noRepeatingDigits = num =>
      !''.match.call(num, /./g).some(function(n) {
        return this[n] = Object.hasOwn(this, n);
      }, {});
    
    // или
    
    const noRepeatingDigits = num =>
      !Array.from(num.toString()).sort().find((n, i, a) => n === a[i + 1]);

    Теперь массив. Можно удалить ненужное из существующего:

    for (let i = 0; i < arr.length; i++) {
      if (!noRepeatingDigits(arr[i])) {
        for (let j = i--; ++j < arr.length; arr[j - 1] = arr[j]) ;
        arr.pop();
      }
    }
    
    // или
    
    arr.reduceRight((_, n, i, a) => noRepeatingDigits(n) || a.splice(i, 1), null);
    
    // или
    
    arr.splice(0, arr.length, ...arr.filter(noRepeatingDigits));
    
    // или
    
    arr.length -= arr.reduce((acc, n, i, a) => (
      a[i - acc] = n,
      acc + !noRepeatingDigits(n)
    ), 0);

    Или собрать новый:

    const newArr = arr.filter(noRepeatingDigits);
    
    // или
    
    const newArr = [];
    for (const n of arr) {
      if (noRepeatingDigits(n)) {
        newArr.push(n);
      }
    }
    
    // или
    
    const newArr = [];
    for (let i = 0; i < arr.length; i++) {
      if (noRepeatingDigits(arr[i])) {
        newArr[newArr.length] = arr[i];
      }
    }
    
    // или
    
    const newArr = (function get(i, n = arr[--i]) {
      return i >= 0
        ? get(i).concat(noRepeatingDigits(n) ? n : [])
        : [];
    })(arr.length);
    Ответ написан
    1 комментарий
  • Как сделать равномерное слияние многомерного массива?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Кого надо объединить:

    const arrs = [
      [ 1, 2, 3, 4 ],
      [ 5, 6, 7, 8 ],
      [ 9, 10, 11 ],
    ];

    Объединяем:

    const result = [];
    const max = Math.max(...arrs.map(n => n.length));
    const index = Array(arrs.length).fill(0);
    
    for (let i = 0; i < max; i++) {
      for (let j = 0; j < index.length; j++) {
        if (index[j] < arrs[j].length) {
          result[result.length] = arrs[j][index[j]++];
        }
      }
    }

    или

    const result = arrs
      .reduce((acc, arr) => (
        arr.forEach((n, i) => (acc[i] ??= []).push(n)),
        acc
      ), [])
      .flat();
    Ответ написан
    Комментировать
  • Как отфильтровать элементы li по объекту?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const li = Array.prototype.filter.call(
      document.querySelector('ul').children,
      function(n) {
        return this.every(([ k, v ]) => v === n.querySelector(`.${k}`).innerText);
      },
      Object.entries(items)
    );

    или

    const li = [];
    
    COLLECT_LI:
    for (const n of document.querySelectorAll('li')) {
      for (const k in items) {
        if (Object.hasOwn(items, k) && items[k] !== n.querySelector('.' + k).textContent) {
          continue COLLECT_LI;
        }
      }
    
      li.push(n);
    }
    Ответ написан
    4 комментария
  • Почему при обновлении состояния не рендерится компонент?

    0xD34F
    @0xD34F Куратор тега React
    Не выдумывайте, всё рендерится.

    Почему видимых изменений нет? Потому что обновляете одно состояние, а рендер выполняете на основе другого, items в App и items в ElementsList - это разные массивы. Не надо никакого useState в ElementList, выполняйте рендер на основе prop'а; метод onDelete перенесите в App и тоже передавайте в ElementList через props.
    Ответ написан
    1 комментарий
  • Миграция на VUE 3, не работает роутер?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Для vue 3 предназначена четвёртая версия роутера, а не третья:

    - <script src="https://unpkg.com/vue-router@3/dist/vue-router.js"></script>
    + <script src="https://unpkg.com/vue-router@4/dist/vue-router.global.js"></script>

    А вот это надо вырезать:

    import VueRouter from 'vue-router'
    Ответ написан
  • Как получить данные маркеров, которые находятся в области видимости карты (vue-yandex-map)?

    0xD34F
    @0xD34F Куратор тега Яндекс.Карты
    Подписаться на события инициализации карты и изменения её границ. Получить границы видимой области карты. Перебрать массив данных, на основе которых создаются маркеры, проверяя, как координаты маркеров соотносятся с границами видимой области. Как-то так:

    <yandex-map
      ref="map"
      @map-was-initialized="onBoundsChange"
      @boundschange="onBoundsChange"
      ...
    >
      <ymap-marker
        v-for="n in markersData"
        ...
      >

    methods: {
      onBoundsChange() {
        const bounds = this.$refs.map.myMap.getBounds();
        this.markersData.forEach(n => {
          if (
            bounds[0][0] < n.coords[0] && n.coords[0] < bounds[1][0] &&
            bounds[0][1] < n.coords[1] && n.coords[1] < bounds[1][1]
          ) {
            // ...
          }
        });
      },
      ...
    Ответ написан
    Комментировать
  • Как объединись значения из двух массивов в один?

    0xD34F
    @0xD34F
    $extract = fn($keys, $item) => array_combine($keys, array_map(fn($k) => $item[$k], $keys));
    
    $grouped = [];
    $productKeys = [ 'product_id', 'sku', 'quantity' ];
    $orderKeys = array_diff(array_keys($arr[0] ?? []), $productKeys);
    
    foreach ($arr as $n) {
      $id = $n['order_id'];
      $grouped[$id] ??= $extract($orderKeys, $n);
      $grouped[$id]['products'][] = $extract($productKeys, $n);
    }
    Ответ написан
  • Почему не получается передать пользовательское событие родительскому компоненту?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Всё получается, всё передаётся.

    метод testemit компонента question не отрабатывает

    А должен? Нет, не должен - вы его нигде не вызываете. Как и не используете в качестве обработчика событий - вижу, что $emit('testemit') есть, а вот @testemit="testemit" отсутствует.
    Ответ написан
  • Как искать по своим вопросам или ответам?

    0xD34F
    @0xD34F
    Поиск тут сильно так себе, так что искать лучше через гугл. Лично я использую запрос следующего вида:

    site:qna.habr.com <имя-пользователя> <тег> <чего ищем>

    Отобрать вопросы/ответы - подписываетесь на интересующие вас теги, заходите в профиль, раздел "подписки", подраздел "теги", появится список тегов - рядом с каждым будут ссылки на списки соответствующих вопросов и ответов (конечно, если таковые у вас есть). Но, разумеется, можно и напрямую пройти, если знать, как нужный url выглядит. Вот ваш laravel, например.
    Ответ написан
    3 комментария
  • Как удалять теги из массива, которые уже не используются?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Зайдите с другой стороны - вместо удаления того, чего больше нет, получите то, что есть, и замените массив:

    tags = Array.from(new Set(data.flatMap(n => n.tags)));

    Если массив по какой-то причине заменять нельзя, тогда удалите все элементы из существующего и запишите новые:

    tags.splice(0, tags.length, ...new Set(data.flatMap(n => n.tags)));
    Ответ написан
    Комментировать
  • Как очищать все поля формы поиска, кроме одного?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Сделать observable объект, содержащий ссылку на экземпляр компонента, в котором последний раз осуществлялся пользовательский ввод. Установить наблюдение за этой ссылкой, и если значение не совпадает с текущим экземпляром - сбрасывать значение input'а:

    const lastEdited = Vue.observable({ instance: null });

    props: [ 'value' ],
    methods: {
      onInput(e) {
        this.$emit('input', e.target.value);
        lastEdited.instance = this;
      },
    },
    created() {
      this.$watch(() => lastEdited.instance, val => {
        if (val && val !== this) {
          this.$emit('input', '');
        }
      });
    },
    beforeDestroy() {
      if (lastEdited.instance === this) {
        lastEdited.instance = null;
      }
    },

    <input :value="value" @input="onInput">

    https://jsfiddle.net/jLw096zm/

    Или можно шину событий применить. Если случилось изменение input'а пользователем, кидать событие, в качестве аргумента передавать ссылку на экземпляр компонента, в обработчике сравнивать значение аргумента и this, если не совпали - очищать input:

    const eventBus = new Vue();

    props: [ 'value' ],
    methods: {
      onInput(e) {
        this.$emit('input', e.target.value);
        eventBus.$emit('clear-other-inputs', this);
      },
    },
    created() {
      const clear = e => e !== this && this.$emit('input', '');
      eventBus.$on('clear-other-inputs', clear);
      this.$on('hook:beforeDestroy', () => eventBus.$off('clear-other-inputs', clear));
    },

    <input :value="value" @input="onInput">

    https://jsfiddle.net/jLw096zm/1/
    Ответ написан
    Комментировать
  • Почему не удается получить актуальное значение переменной из Pinia в компоненте?

    0xD34F
    @0xD34F Куратор тега Vue.js
    имеется pinia такого вида
    <...>
    export const useSearchStore = () => {

    Ну и где тут Pinia? Где вызов defineStore?

    const { search, updateSearchQuery } = useSearchStore();

    Так нельзя:

    Note that store is an object wrapped with reactive, meaning there is no need to write .value after getters but, like props in setup, we cannot destructure it

    Вы вообще как, документацию пробовали открывать?

    Исправляем:

    Стор:

    const useSearchStore = defineStore('search', () => {
      const search = ref('');
      const setSearch = val => search.value = val;
      return { search, setSearch };
    });

    Компонент search input:

    const searchStore = useSearchStore();
    const search = computed({
      get: () => searchStore.search,
      set: searchStore.setSearch,
    });

    <input v-model.trim="search">

    Компонент search:

    Не надо слушать никаких событий из search input, не надо трогать стор, уберите это всё отсюда.

    Корневой компонент:

    const searchStore = useSearchStore(); 
    watch(() => searchStore.search, val => console.log(val));
    Ответ написан