owned mediaウェブ制作に役立つコンテンツを発信中!

JavaScriptで配列やオブジェクトの操作に使えるTips

最終更新日: Update!!
APIなどと連携してデータを取り扱うようなフロントエンドの開発では、配列操作の処理を行うことが多々あります。JavaScriptでは基本的な配列操作のメソッドは色々と充実していますが、こんな場合にどうしれば?というケースもあったりします。そんな時に使えるテクニックをまとめてみたいと思います。   当サイトでは過去記事にJavaScriptの配列操作についてのメソッドをまとめていますので、基本操作などはこちらをご参考ください。 (過去記事) JavaScriptで配列を扱う時によく使うメソッド#1【配列の作成・値や総数の取得・空判定など】 JavaScriptで配列を扱う時によく使うメソッド#2【配列の値に対して繰り返し処理を行う】 JavaScriptで配列を扱う時によく使うメソッド#3【配列に値が含まれるか・値のインデックスを検索する】 JavaScriptで配列を扱う時によく使うメソッド#4【配列から条件に合う値を取得・フィルタリングする】 JavaScriptで配列を扱う時によく使うメソッド#5【配列の値から別の配列を作成・単一の値を算出する】 JavaScriptで配列を扱う時によく使うメソッド#6【配列の値を追加・削除する】 JavaScriptで配列を扱う時によく使うメソッド#7【配列の値を切り取る・値を差し替える・配列を合体する】 JavaScriptで配列を扱う時によく使うメソッド#8【配列の値を並び替える・ランダムにシャッフルする】 JavaScriptで配列を扱う時によく使うメソッド#9【オブジェクトを配列に変換する】 JavaScriptで配列を扱う時によく使うメソッド#10【配列を文字列にする・多次元配列をフラットにする】  
スプレッド演算子で配列を展開して複製(シャローコピー)や結合をする
複数の配列を扱う場合や配列を代入する場合などに便利なのが「スプレッド演算子」です。連続したドットを3つ頭につけるのですが、これをすると、配列を展開して複数の値の集合体として扱うことができます。配列ではなく展開された値となるので、そのまま配列の値に入れることで複数の配列結合もArray.concat()といったメソッドを使用することなくなど簡単に書くことができます。また配列の複製にも使えます。ただしスプレッド演算子を使った複製はシャローコピーとなりますので、複製した配列の値を更新すると元の配列の値も変わってしまうので注意が必要です。
const
  array = [1, 2, 3],
  other = [4, 5, 6],
  cloned = [...array],
  marged = [...array, ...other];

// cloned
[1, 2, 3]

// marged
[1, 2, 3, 4, 5, 6]
   
配列やオブジェクトのディープコピー
先ほどのスプレッド演算子を使った方法などでシャローコピーとして複製された配列や、Array.push()などのメソッドにより元の配列の値も更新されてしまうことがあります。配列やオブジェクトは代入時に参照渡しとなるので、元のデータが影響することに注意が必要です。それを避けるためには完全に別物のデータとして複製する必要があり、その作業は「ディープコピー」と呼ばれます。配列やオブジェクトをディープコピーする方法はいくつかありますが、「JSON.parse()メソッド」と「JSON.stringify()メソッド」を使う方法が簡単です。コピー元の配列やオブジェクトをJSON文字列に変更してから、JSONオブジェクトに変換するという流れになります。
let array = [
  { 1: 'one' }, { 2: 'two' }, { 3: 'three' }
];
const
  shallow = [...array],
  deep = JSON.parse(JSON.stringify(array));
array[0][1] = 'いち';

// shallow
[ { 1: 'いち' }, { 2: 'two' }, { 3: 'three' } ]

// deep
[ { 1: 'one' }, { 2: 'two' }, { 3: 'three' } ]
  このようにシャローコピーの配列は代入された値が反映されていますが、ディープコピーした配列は別のデータとして扱われるため、元の値に代入しても更新されることはありません。ただし、この方法では、一旦文字列に変換する都合上、値の中にundefinedがある場合など一部完全にコピーできないこともありますので注意が必要です。その場合にはArray.map()メソッドなどでもう少し工夫する必要がありそうですね。    
map()メソッドを配列の逆方向から処理
配列処理の中でもArray.map()メソッドもよく使う便利なものですが、配列の後ろから処理していきたいということもあります。Array.reverse()メソッドを使うのですが、そのまま使うと元の配列を変えてしまうため、処理の方向が交互に変わってしまい意図しない順番になってしまいます。そこで、「array.slice(0)」で一旦シャローコピーとして複製したもの逆方向に変えてからmapメソッドを実行します。
const 
  array = [1, 2, 3, 4, 5],
  result = array.slice(0).reverse().map((value) => {
    return `count ${value}`;
  });

// result
['count 5', 'count 4', 'count 3', 'count 2', 'count 1']
   
累計を求める
見積もり計算をするときなどに、項目ごとの小計を出す際に累計を求めることが必要になることもあります。全ての合計値を出す場合には、Array.reduce()メソッドを使うことで算出できますが、累計の場合にはその時点での値を記録しておくので、合計値を出しながら別の配列に値を格納していきます。こうすることで累計値を出すことができます。また、reduceメソッドは初期値がない場合にエラーが出るので、第二引数に初期値を入れておくのも忘れずに。
const
  array = [1, 2, 3, 4, 5],
  result = [],
  calculate = array.reduce((prev, current) => {
    result.push(prev + current);
    return prev + current;
  }, null);

// result
[1, 3, 6, 10, 15]
   
直前の値との処理
もう一つ計算系の処理でたまにあるのが、累計ではなくしりとりのような要領で直前の値のみを処理の対象とするケースです。Array.reduce()メソッドでの累計や合計は配列のループ処理に合わせるので全ての値を対象としますが、ここでは直前の値のみを対象にしないといけません。ですので、対象となる値用の変数を用意して、Array.ForEach()メソッドでその値に直前の値を代入して毎回更新される処理を行うようにしておきます。ループに合わせてこの変数に格納される値が変わるため、直前の値との処理が可能になります。
const
  array = ['北', '東', '南', '西'],
  result = [];
let added = null;
array.forEach((value, index) => {
  if(index !== 0) {
    result.push(added + value);
  }
  added = value;
});

// result
['北東', '東南', '南西']
   
複数の配列を値の並び順に合わせた1つの多次元配列にまとめる
配列の並び替え系の処理で、複数の配列を値の並び順に合わせながら多次元配列にまとめていく方法です。1つの配列の並び替えであれば、Array.map()メソッドや、Array.sort()メソッドが思い当たりますが、複数の場合には少し複雑になります。こちらもループ処理の中で並び替えながら別の配列を用意して、そこにArray.push()メソッドで値を追加していく形になりますが、元の値の並び順に合わせるためにループのインデックスをキーに指定することで対応できます。ループする回数は対象となる配列で最も多い回数にするため、あらかじめ配列の長さの最大値を取得しておきます。
const
  array1 = [1, 2, 3, 4],
  array2 = [1, 2, 3],
  array3 = [1, 2, 3, 4, 5],
  count = Math.max(array1.length, array2.length, array3.length),
  result = [];
for(let index = 0; index < count; index++) {
  result.push([
    array1[index] !== undefined ? array1[index] : null,
    array2[index] !== undefined ? array2[index] : null,
    array3[index] !== undefined ? array3[index] : null,
  ]);
}

// result
[
  [1,1,1],
  [2, 2, 2],
  [3, 3, 3],
  [4, null, 4],
  [null, null, 5]
]
  別のプログラミング言語やJavaScriptのライブラリですと、zip関数といった名前でこういった処理が用意されているようですね。そのうち標準の組み込みメソッドとして用意される時が来るかもしれませんね。    
配列から値を指定の数でランダムに取得する
過去記事「JavaScriptで配列を扱う時によく使うメソッド#8【配列の値を並び替える・ランダムにシャッフルする】」では配列の並びをランダムに並べ替えるという処理を紹介していますが、同じように配列からランダムに値を取得するということもあります。いわゆるガシャのような機能ですね。先述の記事にある方法でランダムに並べ替えてから順番に指定の数だけ取得する方法もありますが、今回は配列の並び替えをせずに別の方法で見ていきたいと思います。並び替え同様に基本的にランダムに処理を行う場合には乱数を取得する必要がありますので、今回も配列の長さの範囲に合わせて乱数を生成しておきます。そしてその乱数を配列のインデックスに使うことで、ランダムにn番目の値を取得することができます。そして重要なのは、一度取得したものは除外しておかないと重複してしまう可能性もあるので、取得したらその分配列も減らしておくようにします。(重複がOKならこの処理は不要になりますね)
const
  array = ['りんご', 'みかん', 'いちご', 'ぶどう', 'もも', 'なし'],
  count = 3,
  result = [];
for(let index = 0; index < count; index++) {
  const random = Math.floor(Math.random() * array.length);
  result.push(array[random]);
  array.splice(random, 1);
}

// result
['いちご', 'りんご', 'ぶどう']
   
複数の値を持つオブジェクトをオブジェクト型の配列にする
最後は、オブジェクトから配列への変換で、複数の値を持つオブジェクトをキー&バリューの形を維持しながらオブジェクト型の配列に変えていきます。オブジェクトから配列への変換は、過去記事「JavaScriptで配列を扱う時によく使うメソッド#9【オブジェクトを配列に変換する】」にもまとめていますが比較的によく使うもので、今回はその応用として、オブジェクトから配列に変換したものをArray.map()メソッドを使って、オブジェクト型の配列に変換している形になります。その際には返り値としてオブジェクトにするためにキーと値を取得する必要がありますが、mapメソッドを実行する前に、Object.entries()メソッドにより、キーと値が含まれた配列の集合になっていますので、引数でその形のまま受け取るようにします。こうすることで返り値の中で引数を使ってオブジェクトの形に戻すことが可能になります。
const 
  object = {
    tokyo: '東京',
    osaka: '大阪',
    nagoya: '名古屋' 
  },
  result = Object.entries(object).map(([key, value]) => {
    return {
      [key]: value
    };
  });

// result
[
  { 'tokyo': '東京' },
  { 'osaka': '大阪' },
  { 'nagoya': '名古屋' },
]
  オブジェクト型でまとまっているよりも、配列型に変えておいた方が何かとデータとして扱いやすいので、結構使い所はあるのではないでしょうか。  
  今回はJavaScriptで配列やオブジェクトの値を操作するときに使える方法をまとめてみました。ウェブアプリケーションを開発するときに、APIやバックエンド側で用意されたデータを受け取る際に必要となる処理ですが、要件によっては工夫しないと実現が難しいものもありますので、ぜひ色々と覚えておきたいですね。   (参考にさせて頂いたサイト) Object.entries() を使ってObject → 配列変換 シャローコピー・ディープコピーとは
  • はてなブックマーク
  • Pocket
  • Linkedin
  • Feedly

この記事を書いた人

Twitter

sponserd

    keyword search

    recent posts

    • Twitter
    • Github
    contact usscroll to top
      • Facebook
      • Twitter
      • Github
      • Instagram