Object.values() не обновляет переменную

У меня есть переменная G.playerStatsDifference, определенная как массив объектов:

playerStatsDifference: [{
        carpenter: 0,
        wood: 0,
        gunman: 0,
        gunpowder: 0,
        merchant: 0,
        gold: 0,
        fleet: 0,
        flagship: 0,
    }, {
        carpenter: 0,
        wood: 0,
        gunman: 0,
        gunpowder: 0,
        merchant: 0,
        gold: 0,
        fleet: 0,
        flagship: 0,
    }]

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

Моя функция для вычисления разницы:

const oldPlayerStats = JSON.parse(JSON.stringify(G.playerStats));
statsDifference(G, oldPlayerStats);  

for (let p = 0; p < 2; p++) { 
    for (let s = 0; s < 8; s++) { 
        Object.values(G.playerStatsDifference[p])[s] = Object.values(G.playerStats[p])[s] - Object.values(oldPlayerStats[p])[s];
    }    
}

Ожидаемый результат будет иметь playerStatsDifference

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

Вот некоторые из этих тестов с правильными расчетами:

console.info("Current wood is " + Object.values(G.playerStats[0])[1]); //Current wood is 5
console.info("Old wood is " + Object.values(oldPlayerStats[0])[1]); //Old wood is 10
console.info(Object.values(G.playerStats[0])[1] - Object.values(oldPlayerStats[0])[1]); //-5

Я подумал, может быть, я делаю что-то не так с петлями, поэтому потом попробовал следующее:

Object.values(G.playerStatsDifference[0])[1] = Object.values(G.playerStats[0])[1] - Object.values(oldPlayerStats[0])[1];

Однако и это не сработало. Сказав это, следующее работает:

G.playerStatsDifference[0].wood = Object.values(G.playerStats[0])[1] - Object.values(oldPlayerStats[0])[1];

Так что, похоже, у меня проблемы с Object.values на G.playerStatsDifference. Любая идея о том, почему это так и как я могу запустить это через цикл?

=====

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

Предполагается, что значение G.playerStatsDifference отслеживает разницу между предыдущим значением G.playerStats и текущим значением G.playerStats.

Для этого я устанавливаю значение oldPlayerStats равным G.playerStats, а затем обновляю G.playerStats до нового значения.

Затем мне нужно просмотреть массив объектов и вычесть значение G.playerStats из oldPlayerStats. Это даст значение G.playerStatsDifference

Вот для чего нужен цикл, чтобы пройтись по каждому ключу объекта и выполнить вычисления.

Надеюсь, это внесет некоторую ясность. Извините за плохо сформулированный вопрос.

Object.values — это статический метод корневого Object класса. Вы не можете назначить его. Также нет смысла вызывать его с результатом такой математической операции, как вычитание.
Jared Smith 20.11.2022 16:22

Итак, playerStatsDifference увеличивается в размерах с каждым новым объявлением playerStats?

Andrew Parks 20.11.2022 16:22

@AndrewParks Нет, значения заменяются/обновляются.

evilgenious448 20.11.2022 16:22

Кажется, у вас повсюду есть Object.values. Это не то, что вы думаете.

James 20.11.2022 16:23

@JaredSmith Это для того, чтобы указать на это, какие есть альтернативные подходы?

evilgenious448 20.11.2022 16:23

Итак, playerStatsDifference содержит два последних набора различий?

Andrew Parks 20.11.2022 16:23

@AndrewParks Он должен содержать разницу между текущим значением и старым значением. Как отмечают другие, похоже, я неправильно использую object.values

evilgenious448 20.11.2022 16:24

@evilgenious448 Я не хочу обижаться, когда говорю это (все мы с чего-то начинаем, и вы не станете лучше, если не будете пытаться), но ваш подход настолько сломан, что я не могу понять, чего вы пытаетесь достичь. Может быть, отредактировать вопрос, чтобы включить больше фона?

Jared Smith 20.11.2022 16:25

Но если он содержит разницу между текущим и старым, почему там два объекта? Почему не только один?

Andrew Parks 20.11.2022 16:25

@JaredSmith Хорошо, я отредактирую вопрос, чтобы сделать его более понятным. Без обид, спасибо за вежливость в ответе :)

evilgenious448 20.11.2022 16:26
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
10
65
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

// for testing purposes, create an object with some random stats
const randomPlayerStats = () => Object.fromEntries(
  ['carpenter','wood','gunman','gunpowder','merchant','gold','fleet','flagship']
    .map(k=>[k,Math.random()*10|0]));

// array of the last player stats recorded for each player
let lastPlayerStats = [];

// create a new object from the existing object, subtracting each entry
// from the old object from the entry from the new object
// note: uses the ?? operator so that if there is no last object yet,
// the last object value will be treated as being zero
const difference = (playerStats, lastPlayerStats) => {
  let r = Object.fromEntries(Object.entries(playerStats).map(([k,v])=>
    [k, v-(lastPlayerStats?.[k]??0)]));
  lastPlayerStats = playerStats;
  return r;
};

// simulate 5 rounds of the game, with 2 players in the game
const playerCount = 2;
const simulatedRounds = 5;
for(let c=0;c<simulatedRounds;c++) {
  let playerStats = [...Array(playerCount).keys()].map(i=>randomPlayerStats());
  let playerStatsDifference = playerStats.map((s,i)=>
    difference(s, lastPlayerStats[i]??{}));
  console.info('playerStats:');
  console.info(playerStats);
  console.info('playerStatsDifference:');
  console.info(playerStatsDifference);
}

Привет, @Andrew Parks, можешь ли ты отредактировать ответ, чтобы вывести разницу в той же структуре, что и статистика. Итак { "carpenter": 4, "wood": 0, "gunman": 6, "gunpowder": 8, "merchant": 0, "gold": 8, "fleet": 5, "flagship": 5 }

evilgenious448 20.11.2022 16:48

@evilgenious448 ой, исправлено

Andrew Parks 20.11.2022 16:54

@AndrewParks, вам действительно нужно добавить какое-то объяснение к этой стене кода. Это не очень полезно в его нынешнем состоянии.

Andy 20.11.2022 17:05

@evilgenious448 Наконец-то я понял, почему в playerStatsDifference было несколько записей — это многопользовательская игра. Мой ответ теперь работает для многопользовательской игры

Andrew Parks 20.11.2022 17:08

Привет @Andrew Parks, извините, я должен был объяснить, что это многопользовательская игра с двумя игроками. Спасибо за редактирование вашего ответа. Кажется, он работает именно так, как я хочу. Но ваш код немного нов для меня, поэтому я был бы признателен, если бы вы могли оставить некоторые комментарии и посмотреть, что делают определенные части кода.

evilgenious448 20.11.2022 17:10
Ответ принят как подходящий
const diffBetweenObjectValues = (a, b) => {
  return Object.entries(a).reduce((result, [aKey, aVal]) => {
    result[aKey] = aVal - (b[aKey] ?? 0);
    return result;
  }, {});
}

const stats = { a: 1, b: 2 };
const updatedStats = { a: 1, b: 1 };

// Initial player stats are { a: 1, b: 2 }
const player = { stats: stats, diff: {} };

// Set the diff, value is { a: 0, b: 1 }
player.diff = diffBetweenObjectValues(player.stats, updatedStats);

// Actually update the stats, value is { a: 1, b: 1 }
player.stats = updatedStats;

Обратите внимание, что если ключ присутствует в b, но не a, он игнорируется. Также обратите внимание, что это работает правильно, только если все значения свойств являются числовыми.

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

Ответ на комментарий

Хорошо, давайте добавим еще одну вспомогательную функцию

const zip = (a, b) => a.map((x, i) => [x, b[i]]);

const players = [...]; // array of players
const statUpdates = [...];   // array of stat updates
zip(players, statUpdates).forEach(([player, stats]) => {
  player.diff = diffBetweenObjectValues(player.stats, stats);
  player.stats = stats;
});

Zip объединяет массив игроков и массив обновлений статистики в пары, затем перебирает их с помощью forEach, деструктурирует биты обратно и запускает обновление. Вы также можете просто использовать цикл for, который быстрее, но его труднее читать и легче ошибиться (например, ошибки по одному). Я бы придерживался этой версии до тех пор, пока ваш профилировщик не скажет вам, что она слишком медленная.

Обновление 2

const currentStats = [{ a: 1, b: 2 }, {a: 3, b: 2 }];
const updatedStats = [{ a: 0, b: 1 }, {a: 4, b: 1 }];
const diffedStats = zip(currentStats, updatedStats).map(([current, updated]) => {
  return diffBetweenObjectValues(current, updated);
});

Привет, Джаред, просто пытаюсь обдумать этот ответ. Кроме того, что было бы правильным сделать, чтобы stats был массивом с двумя объектами, где stats[0] — статистика для первого игрока, а stats[1] — статистика для второго игрока.

evilgenious448 20.11.2022 16:58

@evilgenious448 см. мою правку к ответу.

Jared Smith 20.11.2022 17:12

Привет @Jared Smith, извините за задержку с моим ответом, я пытался интегрировать ваше решение в свой код. Значения для stats и updatedStats будут выглядеть следующим образом: const stats = [{ a: 1, b: 2 }, { a: 1, b: 2 }]; const updatedStats = [{ a: 1, b: 1 }, { a: 1, b: 1 }]; Если я правильно понимаю, ваше решение не совсем работает со значениями как с двумя объектами в массиве.

evilgenious448 20.11.2022 17:48

@evilgenious448 Как правило, структурировать код таким образом ошибочно, за исключением разработки игр AAA на C++, и даже не всегда. В объектно-ориентированном коде объект должен владеть своим состоянием, статистика игрока должна быть свойством объекта игрока, а не массивом отдельных сущностей, связанных положением в массиве. Я думаю, что отчасти причина, по которой людям трудно ответить на ваш вопрос, заключается в том, что ни один опытный разработчик не стал бы разрабатывать его таким образом (опять же, без обид).

Jared Smith 20.11.2022 18:06

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

evilgenious448 20.11.2022 18:08

@evilgenious448 Я предполагаю, что он был разработан кем-то из разработчиков игр AAA с использованием C++, где такое структурирование имеет смысл для повышения частоты промахов кеша. Но это не C++, это Javascript, и очень немногие написанные от руки JS-игры нуждаются в таком уровне производительности. Насколько мне известно, высокопроизводительные JS-игры обычно пишутся на чем-то другом (например, на C++ с SDL, C# на Unity), а затем компилируются в нечитаемый человеком JS или даже WASM.

Jared Smith 20.11.2022 18:12

Ах хорошо, это ценно знать. Возможно, для следующей игры я рассмотрю более подходящий фреймворк. Можете ли вы изменить свой ответ, чтобы он соответствовал структуре массива/объекта?

evilgenious448 20.11.2022 18:14

Давайте продолжим обсуждение в чате.

evilgenious448 20.11.2022 18:33

Просто хочу добавить последний комментарий о том, что @Jared Smith определенно является тем, на кого StackOverflow нужно больше людей, чтобы быть похожими. Делал все возможное, чтобы помочь мне, даже когда я, вероятно, откусывал немного больше, чем мог прожевать, он был терпелив и вежлив и помог мне добраться туда, где мне нужно быть. Хотел бы я дать ему все 500 баллов, он их заслуживает.

evilgenious448 20.11.2022 19:01

Другие вопросы по теме