【解決】二次元配列の要素同士が連動(?)する条件と回避方法が分かりません。

トピックの投稿

返信へ
アバター
サブちゃんB
記事: 27
登録日時: 2018年3月06日(火) 01:09

【解決】二次元配列の要素同士が連動(?)する条件と回避方法が分かりません。

投稿記事 by サブちゃんB »

RPGツクールMZの質問というよりJavaScriptの質問になってしまいそうで恐縮ですが。

二次元配列で現在所持しているアイテムを管理しています。
複数の条件が重複する行のアイテム個数を1行の個数へひとまとめにして残りの行へ0を代入する処理を、
プラグインから実行する関数によって試みています。

まとめ処理の前段階として二次元配列を次のようにソートしています。

コード: 全て選択

      $gameVariables.value(DBvalNum).sort(function(a, b) {
        //1列目は昇順。
        if (Number(a[0]) > Number(b[0])) return 1;
        if (Number(a[0]) < Number(b[0])) return -1;
        //2列目も昇順。
        if (Number(a[1]) > Number(b[1])) return 1;
        if (Number(a[1]) < Number(b[1])) return -1;
        // 4列目も昇順。
        if (Number(a[3]) > Number(b[3])) return 1;
        if (Number(a[3]) < Number(b[3])) return -1;
        //3列目をいったん降順に。
        if (Number(a[2]) > Number(b[2])) return -1;
        if (Number(a[2]) < Number(b[2])) return 1;
        //3列目も昇順。
        if (Number(a[2]) > Number(b[2])) return 1;
        if (Number(a[2]) < Number(b[2])) return -1;
        return 0;
      });
現在確認できる限りこれでソートはうまくいっているようなので、次に重複行の統合の処理を試みます。

コード: 全て選択

      for (let i = 0; i < $gameVariables.value(DBvalNum).length -1; i++) {
        console.log("実行i" + i);
        console.log($gameVariables.value(DBvalNum)[i][2]);
        if($gameVariables.value(DBvalNum)[i][2] == 0){
          console.log("i側何もしない");
          console.log($gameVariables.value(DBvalNum)[i][2]);
          //先頭の個数が0なら何もしない
        }else{
          if($gameVariables.value(DBvalNum)[i][0] == $gameVariables.value(DBvalNum)[i+1][0] && $gameVariables.value(DBvalNum)[i][1] == $gameVariables.value(DBvalNum)[i+1][1] && $gameVariables.value(DBvalNum)[i][3] == $gameVariables.value(DBvalNum)[i+1][3] && $gameVariables.value(DBvalNum)[i][2] > 0){
            let tmpForAdd = []; //要素同士の代入や加算の際に一時的に配列をコピーするためのローカル変数。
            for(let j = i + 1; j  < $gameVariables.value(DBvalNum).length -1 +1; j++){
              if($gameVariables.value(DBvalNum)[i][0] == $gameVariables.value(DBvalNum)[j][0] && $gameVariables.value(DBvalNum)[i][1] == $gameVariables.value(DBvalNum)[j][1] && $gameVariables.value(DBvalNum)[i][3] == $gameVariables.value(DBvalNum)[j][3] && $gameVariables.value(DBvalNum)[j][2] > 0){
                if($gameVariables.value(DBvalNum)[i][2] == 0){
                  //先頭の個数が0なら何もしない
                  console.log("j側何もしない");
                  console.log($gameVariables.value(DBvalNum)[i][2]);
                }else{
                  console.log("j加算前v81[i][2] ="+$gameVariables.value(DBvalNum)[i][2] + ": i= " + i);
                  console.log("j加算前v81[j][2] ="+$gameVariables.value(DBvalNum)[j][2] + ": j= " + j);
                  tmpForAdd = Array.from($gameVariables.value(DBvalNum));
                  $gameVariables.value(DBvalNum)[i][2] += tmpForAdd[j][2];
                  console.log("j加算後v81[i][2] ="+$gameVariables.value(DBvalNum)[i][2] + ": i= " + i);
                  console.log("j加算後v81[j][2] ="+$gameVariables.value(DBvalNum)[j][2] + ": j= " + j);
                  $gameVariables.value(DBvalNum)[j][2] = 0;
                  console.log("jへ0代入後v81[i][2] ="+$gameVariables.value(DBvalNum)[i][2] + ": i= " + i);
                  console.log("jへ0代入後v81[j][2] ="+$gameVariables.value(DBvalNum)[j][2] + ": j= " + j);
                  console.log("実行j" + j);
                  console.table($gameVariables.value(DBvalNum));
                  //retry0229 = 1; //行削除が行われたので処理が終わったらループ冒頭へ戻る
                }
               }
            }
          }
        }
      }
これがうまくいけばあとは3列目が0の行をfilterなどで消せるかと思うのですが、どうもうまくいきません。

たとえば
【解決】二次元配列の要素同士が連動(?)する条件と回避方法が分かりません。 スクリーンショット 2022-12-13 15.10.28.png
↑こちらの二次元配列を処理した場合、最終結果は
【解決】二次元配列の要素同士が連動(?)する条件と回避方法が分かりません。 スクリーンショット 2022-12-13 15.14.58.png
↑このように悲惨です。
【解決】二次元配列の要素同士が連動(?)する条件と回避方法が分かりません。 スクリーンショット 2022-12-13 15.18.07.png
↑最初のソートはうまくいっているようなのですが、
【解決】二次元配列の要素同士が連動(?)する条件と回避方法が分かりません。 スクリーンショット 2022-12-13 15.15.43.png
1行目の3列目へ2行目の3列目の値を加算しようとする時点で連動(?)しており、
2行目の3列目へ0を代入すると1行目の3列目まで0になってしまって、
以降の処理がおかしくなるようです。

コード: 全て選択

                  console.log("j加算前v81[i][2] ="+$gameVariables.value(DBvalNum)[i][2] + ": i= " + i);
                  console.log("j加算前v81[j][2] ="+$gameVariables.value(DBvalNum)[j][2] + ": j= " + j);
                  tmpForAdd = Array.from($gameVariables.value(DBvalNum));
                  $gameVariables.value(DBvalNum)[i][2] += tmpForAdd[j][2];
                  console.log("j加算後v81[i][2] ="+$gameVariables.value(DBvalNum)[i][2] + ": i= " + i);
                  console.log("j加算後v81[j][2] ="+$gameVariables.value(DBvalNum)[j][2] + ": j= " + j);
                  $gameVariables.value(DBvalNum)[j][2] = 0;
                  console.log("jへ0代入後v81[i][2] ="+$gameVariables.value(DBvalNum)[i][2] + ": i= " + i);
                  console.log("jへ0代入後v81[j][2] ="+$gameVariables.value(DBvalNum)[j][2] + ": j= " + j);
上記のコードでは加算する値の入る配列をいったんtmpForAddへコピーして別の配列として扱うことを試みていますが、当初の直接加算するやり方、concatや[...]を使う方法でも結果は同じでした。
配列そのものは変数のように代入しても参照元は同じなのでconcatなどでコピーしないと分けられないということまでは分かったのですが、要素同士の連動というのは、どうしても調べきれませんでした。
配列の要素同士を足し算するのがダメなのかと思って、ローカル変数を宣言してそちらへ格納してから加算しましたがやはり連動しました。
整数を直接足しても連動しました。

これらの要素を連動させなくするか、あるいは複数行を1まとめにする別の良い方法がございましたら、ご教示頂けますと幸いです。
最後に編集したユーザー サブちゃんB [ 2022年12月13日(火) 21:39 ], 累計 1 回

返信 2 / 2ページ

chro
記事: 107
登録日時: 2021年2月14日(日) 11:26

Re: 【解決】二次元配列の要素同士が連動(?)する条件と回避方法が分かりません。

投稿記事 by chro »

それはもしかして、アイテムを入手する時、同じ配列を追加していませんか?
たぶん、次の結果はtrueになって、同じ参照だと思います。

コード: 全て選択

console.log($gameVariables.value(DBvalNum)[3] === $gameVariables.value(DBvalNum)[4]) //true
$gameVariables.value(DBvalNum)[3][2] === 1; //0でなく、1で代入される
下記やり方で追加すると、同じ参照を追加しているだけになって、質問の件の連動する状態になります。
同じ配列を追加していたから、同じアイテムIDのものが連動してしまったのが、原因だと思います。

コード: 全て選択

var items = [];
var item1 = [0,1,100,98,0,0,0];
var item2 = [0,2,100,0,0,0,0];
items.push(item1,item2,item1,item2);

items[0] === items[2]; //true
items[0][3] = 200;

items[2][3]; //200
解決方法は、
.slice()で新しい配列(単純なディープコピー)を作成するか、直接配列を指定して新たに作成します。

コード: 全て選択

var items = [];
var item1 = [0,1,100,98,0,0,0];
var item2 = [0,2,100,0,0,0,0];
items.push(item1.slice(), item2.slice(), item1.slice(), item2.slice());
items.push([0,1,100,98,0,0,0], [0,2,100,0,0,0,0]);
アバター
サブちゃんB
記事: 27
登録日時: 2018年3月06日(火) 01:09

Re: 【解決】二次元配列の要素同士が連動(?)する条件と回避方法が分かりません。

投稿記事 by サブちゃんB »

完全にchro様の仰るとおりでした!!

console.log($gameVariables.value(DBvalNum)[3] === $gameVariables.value(DBvalNum)[4])
は見事にtrueとなりました。

csvから読み込んだ各アイテム入手時のテーブルを二次元配列として別の$gameVariables.valueに読み込んでおいて、入手時にそこからpush()で追加しておりました。
入手時のpushに.slice()を入れると、直接参照でも正常に動作しました!

うまくいかない処理本体ばかりを見て悩んでおりましたが、まさか入手時のpush()に問題があるとは夢にも思いませんでした。
ここからしてシャローとディープを意識しないといけないとは、自力では気がつくことが出来ませんでした。
お陰様で非常に勉強になりました。
本当にありがとうございました。
アバター
サブちゃんB
記事: 27
登録日時: 2018年3月06日(火) 01:09

Re: 【解決】二次元配列の要素同士が連動(?)する条件と回避方法が分かりません。

投稿記事 by サブちゃんB »

名無し蛙様、chro様、先日は大変貴重な知見をご提供くださいまして、誠にありがとうございました。
お陰様で新作ゲームの開発が非常にうまくいっております。

先日chro様にご教示頂いたパターン1のコードを使って、引き続き条件が一致するアイテムの個数合算をしておりました。
ご教示頂く際に私が非常に重要な点を見落としておりました。
それは、keyIndexが複数存在することです。
想定されるkeyIndexは、0,1,3です。
つまり、1列目(インデックス0)、2列目(インデックス1)、4列目(インデックス3)が一致する場合に、3列目(インデックス2)の数値を合算したいのです。
当然ながら、1つしかないkeyIndexを1に設定した場合、インデックス0とインデックス3が一致しなくても合算されてしまいます。

相談させて頂いたときはインデックス2の値が連動することを解消することのみに注視しており、複数のkeyIndexが存在することについて認識はしていたものの、それくらいは自力で改造出来るだろうと高をくくっていましたが、甘かったです。
ご教示頂いたパターン1、パターン2を自分なりに調べて試行錯誤してみましたが、どうやら今の自分にはkeyIndexが複数存在する場合への改造は無理そうです。

幸いにして当初相談したときに自分で書いた長ったらしいコードが行追加push時の.slice()によりうまく動きましたので、理解の浅いまま追加してしまった余計なディープコピー処理を省いて、今のところなんとか間に合わせることが出来ております。
ご教示頂いたコードを元に合算処理をした方が明らかに演算が速そうなので、ご教示頂いたコードを活かした複数keyIndexの合算にもいつか挑戦してみたいと思います。
名無し蛙
記事: 355
登録日時: 2015年11月23日(月) 02:46
投稿素材

Re: 【解決】二次元配列の要素同士が連動(?)する条件と回避方法が分かりません。

投稿記事 by 名無し蛙 »

多分idの部分で処理を集約させれば帳尻合うと思いますよ。
どのような管理をしているのか知らないので断言は出来ませんが
アイテムデータベースの時点で予め一意のidを振っておいた方が管理が楽だと思いますけどね。
chro氏のコードを借りて改造するならこんな感じですか。
.toString()で配列の参照から文字列に変換する必要がある点に注意すれば、
後は特に難しい所も無いと思います。ついでにparentsも[]から{}に変更しています。

コード: 全て選択

//パターン1
const sumMakeIndexArr = function(arr, keyArray, sumIndex) {
    const parents = {};//親となる、最初に出現したIDがある配列のindex
    arr.forEach((a, i) => {
        const id = keyArray.map(keyIndex => a[keyIndex]).toString();
        let parentIndex = parents[id];
        if (parentIndex === undefined)  {
            //初めて出現するIDの場合、親として扱い、数値はそのまま
            parents[id] = i;
        } else {
            //既にIDが存在した場合は、そのindexへ加算
            arr[parentIndex][sumIndex] += a[sumIndex];
            //親へ合算したため、0を代入
            a[sumIndex] = 0;
        }
    });
};

//サンプル
var arr1= [
    [0,1,100,98,0,0,0],
    [0,2,100,98,0,0,0],
    [0,4,100, 0,0,0,0],
    [0,2,100,98,0,0,0],
    [0,3,100,0,0,0,0],
    [0,2,100,98,0,0,0],
    [1,3,100,0,0,0,0]
];
sumMakeIndexArr(arr1, [0, 1, 3], 2); // (配列, アイテムIDのindex配列, 合計するindex)
console.log(arr1);
アバター
サブちゃんB
記事: 27
登録日時: 2018年3月06日(火) 01:09

Re: 【解決】二次元配列の要素同士が連動(?)する条件と回避方法が分かりません。

投稿記事 by サブちゃんB »

名無し蛙 様、いつもご指導頂きありがとうございます。

ご教示頂きましたコード、見事正常に動作しました!
私が挑戦したときは引数を増やしてif文の条件を増やしてとやってうまくいかずに諦めましたが、
複数のインデックスを配列にするという手があるとは目から鱗です。
連想配列については各データの取得時に意識するのみで自分が組むコードでは積極的に使っていませんでしたが、
使いこなせるようになってスマートなコードを書いてみたいです。

この度も大変貴重な知見をご教示くださいまして、誠にありがとうございました。
今後ともご指導のほど、宜しくお願い致します。
返信する

“MZ:質問”に戻る