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

    0xD34F
    @0xD34F Куратор тега Vue.js
    function shuffle(arr) {
      for (let i = arr.length; i > 1;) {
        const j = Math.random() * (i--) | 0;
        [ arr[i], arr[j] ] = [ arr[j], arr[i] ];
      }
    
      return arr;
    }

    computed: {
      shuffledAnswers() {
        return shuffle(Object.entries(this.questions[this.idx].answers));
      },
      ...

    <div v-for="[ key, answer ] in shuffledAnswers">
      ...

    UPD. Конечно, вопрос был не об этом, но есть ряд замечаний по поводу показанного кода.

    "answers": {

    Пусть это будет массив. Соответственно, правильный ответ будет обозначаться через индекс.

    import usersData from "../questions.json";

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

    selectedAnswer: "",

    Всего один? Этого мало, надо запоминать все полученные ответы.

    count: 5,

    <div v-if="idx < count">

    Если завтра добавятся новые вопросы или удалятся какие-то из существующих, побежите изменять значение этого свойства? Оно не нужно, надо смотреть на длину массива с вопросами. Или, можно сделать вычисляемое свойство, представляющее текущий вопрос (просто извлекаем элемент из массива с вопросами по индексу текущего вопроса), и проверять, существует ли он.

    computed:{
      randomQuestions () {
        usersData.sort(() => Math.random() - 0.5)

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

    :disabled="selectedAnswer != ''"

    Блокировать выбор ответа не надо - вдруг пользователь случайно не туда нажал. Пусть будет возможность изменить сделанный выбор. Это касается не только ответов на текущий вопрос, а всех, т.е., в дополнение к

    <button
      @click="nextQuestion"

    надо сделать такую же кнопку для перехода к предыдущему вопросу.

    document.querySelectorAll("input").forEach((el) => (el.checked = false));

    Не надо лезть в DOM руками. Чтобы при переходе к следующему вопросу сбрасывать выбор, достаточно пересоздавать элементы, назначив их общему предку ключ, зависящий от индекса (:key="индекс_вопроса"). Но это, конечно, костыльное решение. Правильно будет управлять радиокнопками основываясь на данных, через v-model.

    @change="answered($event)"

    answered(e) {
      this.selectedAnswer = e.target.value;
      if (this.selectedAnswer == this.questions[this.idx].correctAnswer) {
        this.correctAnswers++;

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

    https://jsfiddle.net/0L9ayx1u/1/
    Ответ написан
    1 комментарий
  • Почему не могу перейти к странице по прямому адресу?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Потому что документацию не читаете:

    поскольку наше приложение — одностраничное, не сконфигурировав соответствующим образом сервер мы заставим пользователей получать ошибку 404, если они перейдут по http://oursite.com/user/id напрямую

    <...>

    всё, что нужно — единственная "резервная" запись в конфигурации сервера. Если URL не совпадает ни с одним статическим файлом, сервер должен просто отдать index.html, в котором и живёт наше приложение
    Ответ написан
    Комментировать
  • Как код переписать?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const bullshitInsert = str => str
      .replace(/(?<=[2468])(?=[2468])/g, '*')
      .replace(/(?<=[13579])(?=[13579])/g, '-');

    или

    const bullshitInsert = str => Array
      .from(str, (n, i) => (n * str[i - 1] && !((n ^ str[i - 1]) & 1) ? '*-'[n & 1] : '') + n)
      .join('');
    Ответ написан
    3 комментария
  • Как объединить массив с повторяющимися объектами?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Если тупо получать то, что указано в тексте вопроса, тогда

    const result = Object
      .entries(arr.reduce((acc, n) => ((acc[n.name] ??= []).push(n.value), acc), {}))
      .reduce((acc, [ k, v ]) => (acc[k] = v.length === 1 || v, acc), {});

    Но почему

    ThirdName: true

    ??

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

    const result = Object
      .entries(arr.reduce((acc, n) => ((acc[n.name] ??= new Set).add(n.value), acc), {}))
      .reduce((acc, [ k, [...v] ]) => (acc[k] = ~-v.length ? v : v[0], acc), {});
    Ответ написан
    2 комментария
  • Как сократить запись в v-bind?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Не надо indexOf, индексы доступны в v-for без каких-либо дополнительных телодвижений:

    <div v-for="(row, iRow) in rows" class="board__rows">
      <div
        v-for="(col, iCol) in columns"
        v-text="col + row"
        :class="[ [ 'white', 'black' ][(iRow ^ iCol) & 1], 'board__square' ]"
      ></div>
    </div>
    Ответ написан
    Комментировать
  • Почему state не обновляется корректно с использованием React.memo + useCallback?

    0xD34F
    @0xD34F Куратор тега React
    Всё обновляется корректно. Что написали, то и получили.

    Почему всегда рендерятся все экземпляры Child2 - потому что вместо обновления одного элемента data.list вы всегда обновляете все.

    Почему значения сбрасываются на дефолтные - потому что из-за useCallback data у вас всегда имеет начальное значение (ну, то, что было при первом рендеринге, то, которое initialData).

    Переделываем:

    Компонент Parent, обработчик onChange передаваемый в Child1, пусть вместо значения обновляемого свойства получает функцию, которая будет принимать предыдущее значение и возвращать новое:

    React.useCallback((key, val) => {
      setData(data => ({
        ...data,
        [key]: val(data[key]),
      }));
    }, [])

    Ну и переписываем его вызовы внутри Child1:

    onChange('list', list => list.map((n, i) => i === index
      ? { ...n, [prop]: value }
      : n
    ))

    onChange('description', () => e.target.value)
    Ответ написан
  • Как создать фильтр для значения с массивом?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const filter = (arr, conditions) =>
      arr.filter(
        function(n) {
          return this.every(([ k, values ]) => values.some(v => v.includes(n[k])));
        },
        Object
          .entries(conditions)
          .map(n => [ n[0], [].concat(n[1]).map(String) ])
          .filter(n => n[1].length)
      );
    Ответ написан
    8 комментариев
  • Как грамотно использовать this в данном коде?

    0xD34F
    @0xD34F Куратор тега JavaScript
    А лучше всего просто объяснить что бы я в итоге допер сам!

    Объясняю: включаешь голову, думаешь, гуглишь, ещё думаешь, делаешь. Вот так всё просто.
    Ответ написан
    4 комментария
  • Как имея массив уникальных значений создать массив из значений объекта?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Кого будем подставлять вместо отсутствующих значений: const defaultValue = null;.

    Предполагаю что надо использовать reduce.

    Как скажете:

    const result = arr1.reduce((acc, n) => (
      acc.push(arr2.find(m => m.uniq === +n)?.sum ?? defaultValue),
      acc
    ), []);

    Или вот написание циклов руками - ничем не хуже:

    const result = [];
    
    for (const n of arr1) {
      let item = defaultValue;
    
      for (const m of arr2) {
        if (m.uniq == n) {
          item = m.sum;
          break;
        }
      }
    
      result[result.length] = item;
    }

    Ещё рекурсия отлично подойдёт:

    const sums = Object.fromEntries(arr2.map(n => [ n.uniq, n.sum ]));
    const result = (function get(i, n = arr1[i]) {
      return i < arr1.length
        ? [ Object.hasOwn(sums, n) ? sums[n] : defaultValue, ...get(-~i) ]
        : [];
    })(0);

    Главное, чтобы не map. Это ведь совсем не круто - идти наиболее простым и очевидным путём:

    const result = arr1.map(function(n) {
      return this.has(n) ? this.get(n) : defaultValue;
    }, new Map(arr2.map(n => [ `${n.uniq}`, n.sum ])));
    Ответ написан
    1 комментарий
  • Как правильно работать с контекстом в react?

    0xD34F
    @0xD34F Куратор тега React
    Вначале удаляем элемент из массива, затем выбираем новый. Но получается так, что, иногда, выбор случайного элемента массива, происходит до удаления элемента из массива и он выбирает только что удаленный элемент.

    Не "удаляем" - создаётся новый массив, в котором может оказаться (а может и нет) на один элемент меньше. Не "иногда" - всегда выбор нового случайного элемента осуществляется из старого массива.

    Как правильно реализовать такую логику?

    Надо выбирать новый случайный элемент из нового массива, а не из старого. Переносим соответствующий вызов dispatch из обработчика клика в эффект:

    useEffect(() => {
      dispatch({
        type: SET_RND_NUM,
        payload: state.arr[Math.random() * state.arr.length | 0],
      });
    }, [ state.arr ]);
    Ответ написан
  • Как добавить сотрудника в список (таблицу)?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Cannot read properties of undefined (reading 'lastname')

    Понятно, смотрим, где есть попытка чтения свойства lastname.

    async addNewEmployee({ commit }, employee) {
        await axios
            .post(`http://127.0.0.1:8000/api/v1/employees/`, {
                lastname: employee.lastname,

    Тоже понятно, смотрим, где вызывается addNewEmployee и что туда передаётся.

    @submit.prevent="addNewEmployee(employee)"

    А вот здесь непонятно - что такое employee? Такого свойства в компоненте нет. Надо добавить. Его содержимым будет то, что сейчас находится в хранилище - firstname, lastname, number - там, в хранилище, этим данным делать нечего.
    Ответ написан
    1 комментарий
  • Почему возникает TypeError: Illegal invocation при вызове drawImage у canvas'а?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const { drawImage } = canvas.getContext('2d') as CanvasRenderingContext2D;
    
    drawImage(
      ...

    Откуда вы взяли, что методы контекста можно вызывать в отрыве от, м-м-м, КОНТЕКСТА?

    Что у вас происходит, поясняю на пальцах:

    const obj = {
      fucking_value: 666,
      fucking_method() {
        if (!this.hasOwnProperty('fucking_value')) {
          throw 'FUCK OFF';
        }
    
        console.log(this.fucking_value);
      },
    };
    
    const { fucking_method } = obj;
    fucking_method();

    Если вдруг не поняли, то...
    ...вот бы узнать - а какого хрена вы взялись за react (кстати, а при чём он здесь? да не при чём, как и ts), обладая нулевыми знаниями js?

    Ключевое слово this - бегом гуглить, разбираться, что это, зачем, к каким значениям в каких случаях даёт доступ.
    Ответ написан
    4 комментария
  • Как подставить данные из динамического элемента в динамический элемент с помощью JQuery?

    0xD34F
    @0xD34F Куратор тега JavaScript
    document.querySelector('.form').addEventListener('input', e => {
      const data = Array.from(
        e.currentTarget.children,
        n => Array.from(n.querySelectorAll('input'), m => m.value)
      );
    
      document.querySelector('.total').innerText = Array
        .from(document.querySelectorAll('.row'))
        .reduce((acc, n, i) => {
          data[i].forEach((m, j) => n.children[j + 1].innerText = m);
          return acc + (+data[i].at(-1) || 0);
        }, 0);
    });

    или

    const total = document.querySelector('.total');
    const form = document.querySelector('.form');
    const formRows = form.getElementsByClassName('d-flex');
    const tableRows = document.getElementsByClassName('row');
    
    form.addEventListener('input', ({ target: t }) => {
      const inputBox = t.closest('.input-box');
      const formRow = inputBox.parentNode;
      const iRow = Array.prototype.indexOf.call(formRows, formRow);
      const iCol = -~Array.prototype.indexOf.call(formRow.children, inputBox);
    
      tableRows[iRow].cells[iCol].textContent = t.value;
    
      if (iCol === formRow.children.length) {
        total.textContent = Array.prototype.reduce.call(
          formRows,
          (acc, n) => acc + (n.lastElementChild.children[0].value | 0),
          0
        );
      }
    });
    Ответ написан
    4 комментария
  • Как преобразовать древовидный массив в плоский?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Рекурсия есть:

    const getFromTree = (tree, childrenKey, getter = n => n) =>
      Array.isArray(tree)
        ? tree.flatMap(n => [
            getter(n),
            ...getFromTree(n[childrenKey], childrenKey, getter),
          ])
        : [];
    
    // или
    
    function* flatTree(tree, childrenKey) {
      if (
        tree instanceof Object &&
        tree[Symbol.iterator] instanceof Function
      ) {
        for (const n of tree) {
          yield n;
          yield* flatTree(n[childrenKey], childrenKey);
        }
      }
    }

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

    const getFromTree = function(tree, childrenKey, getter = n => n) {
      const result = [];
    
      for (const stack = this(tree); stack.length;) {
        const n = stack.pop();
        result.push(getter(n));
        stack.push(...this(n[childrenKey]));
      }
    
      return result;
    }.bind(x => x instanceof Array ? [...x].reverse() : []);
    
    // или
    
    const flatTree = function*(tree, childrenKey) {
      const stack = [];
    
      for (let [ i, arr ] = this(tree); ++i < arr.length || stack.length;) {
        if (i === arr.length) {
          [ i, arr ] = stack.pop();
        } else {
          yield arr[i];
          stack.push([ i, arr ]);
          [ i, arr ] = this(arr[i][childrenKey]);
        }
      }
    }.bind(x => [ -1, x?.constructor === Array ? x : [] ]);

    Получаем массив:

    // если использовать обычную функцию
    const result = getFromTree(tree, 'children', ({ children, ...n }) => n);
    // или, генератор
    const result = Array.from(flatTree(tree, 'children'), ({ children, ...n }) => n);
    Ответ написан
    1 комментарий
  • Как создать объект с регистронезависимыми ключами?

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

    const obj = new Proxy({}, {
      get(target, key) {
        const lowerKey = key.toLowerCase();
        return target[Object.hasOwn(target, lowerKey) ? lowerKey : key];
      },
      set(target, key, val) {
        target[key.toLowerCase()] = val;
        return true;
      },
      has(target, key) {
        return key in target || key.toLowerCase() in target;
      },
      defineProperty(target, key, descriptor) {
        return Object.defineProperty(target, key.toLowerCase(), descriptor);
      },
      deleteProperty(target, key) {
        return delete target[key.toLowerCase()];
      },
      getOwnPropertyDescriptor(target, key) {
        return Object.getOwnPropertyDescriptor(target, key.toLowerCase());
      },
    });
    Ответ написан
    1 комментарий
  • Как объединить массивы объектов по значениям одного из свойств их элементов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const merge = (key, ...arrs) =>
      Object.values(arrs.flat().reduce((acc, n) => (
        Object.assign(acc[n[key]] ??= {}, n),
        acc
      ), {}));
    
    const result = merge('id', sum, arr1, arr2);

    или

    const merge = (key, ...arrs) =>
      Array.from(arrs.reduce((acc, arr) => arr.reduce((acc, n) => {
        const k = key(n);
        return acc.set(k, Object.assign(acc.get(k) ?? {}, n));
      }, acc), new Map).values());
    
    const result = merge(n => n.id, sum, arr1, arr2);
    Ответ написан
    4 комментария
  • Как правильно реализовывать прелоадеры?

    0xD34F
    @0xD34F Куратор тега Vue.js
    должен быть более лаконичный способ, чем плодить такие переменные

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

    Или это считается нормальной практикой?

    Да, считается. У говнокодеров.
    Ответ написан
    3 комментария
  • Как из массива объектов получить данные по id в другом массиве?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const result = arr.map(function(n) {
      return this[n];
    }, Object.fromEntries(sum.map(n => [ n.id, n.name ])));
    
    // или
    
    const result = arr.map(Map.prototype.get.bind(new Map(sum.map(n => [ n.id, n.name ]))));
    
    // или
    
    const result = arr.map(n => sum.find(m => m.id === n)?.name);

    Но что если в массиве sum отсутствуют некоторые из нужных элементов, а получать undefined внутри массива с результатами не хочется?

    Можно подставлять вместо отсутствующих элементов какое-нибудь другое значение:

    const names = Object.fromEntries(sum.map(n => [ n.id, n.name ]));
    const result = arr.map(n => names[n] ?? `объекта с id=${n} нет`);

    Или, есть вариант получать только то, что существует:

    const names = new Map(sum.map(n => [ n.id, n.name ]));
    const result = arr.reduce((acc, n) => ((n = names.get(n)) && acc.push(n), acc), []);
    Ответ написан
    2 комментария
  • Почему не меняется класс при скролле?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Встречный вопрос - почему вы решили, что класс должен меняться? Может, всё-таки стоит почаще заглядывать в документацию, а?

    app.ifScroll =

    Меняйте эту чушь на ifScroll.value =.
    Ответ написан
    Комментировать