🍞

Javascriptでめっちゃ深いコピーする関数作ってみた

2023/04/25に公開

【23/10/24 日付オブジェクトをうまくコピーできなかったので修正】

javascriptでオブジェクトとか連想配列なんかを深いコピーしたいときによく↓のやつ使ってたんですが、

javascript
/**
* lists:元の配列
* copy:コピー配列
*/
const copy = lists.map( list => ({...list}))

これだと下記のような深い階層のものだと、値が変わってしまうんですよね。

javascript
const lists = [{id:1, users:[{user_id:1},{user_id:2}]},{id:2, users:[{user_id:3},{user_id:4}]}];
const copy = lists.map( list => ({...list}))
copy[0].users[0].user_id = 20;
console.log(JSON.stringify(lists));
console.log(JSON.stringify(copy));

出力結果

javascript
[{"id":1,"users":[{"user_id":20},{"user_id":2}]},{"id":2,"users":[{"user_id":3},{"user_id":4}]}]
[{"id":1,"users":[{"user_id":20},{"user_id":2}]},{"id":2,"users":[{"user_id":3},{"user_id":4}]}]

そこでライブラリを使うなど、いろいろ方法はあるみたいです。
https://zenn.dev/keita_hino/articles/2e91c761f17ce8

自作してみる

ただ、勉強がてら自分で作ってみようということで作ってみました。
それが↓

javascript
const associativeArrayCopy = (lists) => {
  // オブジェクトかどうか判定する関数
  const isObject = (value) => {
     return value !== null && typeof value === 'object'
  }
  // 日付オブジェクトかどうか判定する関数
  const isDate = (value) => {
    return Object.prototype.toString.call(value).slice(8, -1) === 'Date';
  }
  // 再帰的にコピーを返す関数
  const entriesObject = (list) => {
    // オブジェクトじゃなくて値になったら終了
    if(!isObject(list)) return list;
    // オブジェクトの中でも日付型の場合は日付のコピーを行う
    if(isDate(list)) return new Date(list.valueOf());
    // 再帰的にコピーする
    return Object.entries(list).reduce((acc,[key, value]) =>  ({...acc, [key]:associativeArrayCopy(value)}), {});
  }
  // 配列かオブジェクトで分ける
  return Array.isArray(lists) ? lists.map(list=> entriesObject(list)) : entriesObject(lists);      
}

初めの方法を再帰的にやりました。試しに初めの深い階層のものでやってみます。

javascript
const lists = [{id:1, users:[{user_id:1},{user_id:2}]},{id:2, users:[{user_id:3},{user_id:4}]}];
const copy = associativeArrayCopy(lists)
copy[0].users[0].user_id = 20;
console.log(JSON.stringify(lists));
console.log(JSON.stringify(copy));

出力結果

javascript
[{"id":1,"users":[{"user_id":1},{"user_id":2}]},{"id":2,"users":[{"user_id":3},{"user_id":4}]}]
[{"id":1,"users":[{"user_id":20},{"user_id":2}]},{"id":2,"users":[{"user_id":3},{"user_id":4}]}]

間違ってたらごめんなさい。

参考サイト

https://zenn.dev/suin/books/8985cbff87b524e11c2b/viewer/01e63d

Discussion