よく使うJavaScriptの配列操作をわかりやすくまとめてみた
はじめに
特に下記の違いをわかりやすくまとめたつもりです!
map、for、forEachの違い
indexOf、findIndexの違い
オブジェクトの参照渡しとその影響の部分でかなりハマりました...
オブジェクト操作はこちらです。
map
配列の各要素に対して与えられた関数を呼び出し、その結果から新しい配列を生成して返します。
元の配列の要素を変更するわけではなく、新しい配列を生成するため、元の配列は変更されません。
const numbers = [1, 2, 3, 4, 5];
numbers.map(num => num * 2);
// numbersは変更されず、[1, 2, 3, 4, 5]のまま
const doubledNumbers = numbers.map(num => num * 2);
// doubledNumbers [2, 4, 6, 8, 10]になります
オブジェクトの配列を加工して新しい配列を生成する例は以下です。
const objectArray = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
const newArray = objectArray.map(obj => {
return {
id: obj.id,
updatedName: obj.name.toUpperCase()
};
});
// newArrayは以下のようになります。
// [{ id: 1, updatedName: "ALICE" }, { id: 2, updatedName: "BOB" }]
include
特定の要素が配列内に存在するかどうかをチェックするために使用されます。このメソッドは配列内を検索し、指定された要素が見つかれば true を、見つからなければ false を返します。
const fruits = ['apple', 'banana', 'mango', 'orange'];
if (fruits.includes('banana')) {
console.log('バナナが含まれています!');
}
// バナナが含まれています!
配列を特定の条件の場合に内容を変換する時にも使用できます。条件が長くなる場合にはincludeを使用した方がすっきり記述することができます。
const fruits = [
{ name: 'リンゴ', type: '甘い' },
{ name: 'レモン', type: '酸っぱい' },
{ name: 'バナナ', type: 'まろやか' },
{ name: 'グレープフルーツ', type: '酸っぱい' },
{ name: 'メロン', type: '甘い' }
];
// includeを使用しない書き方
const updatedFruits = data.map(item => {
// タイプが「甘い」または「まろやか」であれば、'フルーティー'に変更
if (item.type==='甘い' || item.type==='まろやか' ) {
return { ...item, type: 'フルーティー' };
}
// それ以外の場合は、そのままのオブジェクトを返す
return item;
});
const updatedFruits = data.map(item => {
if (['甘い','まろやか'].includes(item.type)) {
return { ...item, type: 'フルーティー' };
}
// それ以外の場合は、そのままのオブジェクトを返す
return item;
});
// updatedFruitsは
[
{'name': 'リンゴ', 'type': 'フルーティー'},
{'name': 'レモン', 'type': '酸っぱい'},
{'name': 'バナナ', 'type': 'フルーティー'},
{'name': 'グレープフルーツ', 'type': '酸っぱい'},
{'name': 'メロン', 'type': 'フルーティー'}
]
になります
for
配列の要素に対して反復処理を行います。
mapとforEachと違う点は、numbers.lengthを3などに指定すると配列の長さが指定した範囲で繰り返される点かなと思います。
今回の例ではiが0から2までの範囲で繰り返されます。そのため、配列の最初の3つの要素にのみ2倍の処理が行われます
const numbers = [1, 2, 3, 4, 5];
for (let i = 0; i < numbers.length; i++) {
numbers[i] = numbers[i] * 2;
}
// 元の配列(numbers)が[2, 4, 6, 8, 10]になります
const doubledNumbers = [];
for (let i = 0; i < numbers.length; i++) {
doubledNumbers.push(numbers[i] * 2);
}
// doubledNumbersは[2, 4, 6, 8, 10]になります
for (let i = 0; i < 3; i++) {
numbers[i] = numbers[i] * 2;
}
// 元の配列(numbers)が[2, 4, 6, 4, 5]になります
forEach
配列の各要素に対して与えられた関数を呼び出します。
配列の各要素に順番にアクセスし、与えられた関数を呼び出しますが、新しい配列を返しません。
また、forEachを使用してフィルタリングを行うことも可能です。
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((num, index) => {
numbers[index] = num * 2;
});
// 元の配列(numbers)が [2, 4, 6, 8, 10]になります
const newNumbers=numbers.forEach((num, index) => {
numbers[index] = num * 2;
});
// forEach メソッドは、新しい配列を返さないので、newNumbersには何も代入されません
// newNumbersは undefined になります。
新しい配列を返さないので、新しい配列を取得したい場合は、基本的にはmap
メソッドを使用するかpush
してください。
const doubledNumbers = [];
numbers.forEach(num => {
doubledNumbers.push(num * 2);
});
// doubledNumbersは[2, 4, 6, 8, 10]になります
const resultArray = [];
numbers.forEach((num) => {
if (num === 2) {
resultArray.push(num);
}
});
// resultArrayは[2]になります
filter
与えられた条件を満たす要素のみを抽出した新しい配列を生成します。
条件を満たす要素のみを選択するため、mapと同様に新しい配列を生成しますが、各要素を変換するのではなく、単にフィルタリングします。
const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(num => num % 2 === 0);
);
// numbersは[1, 2, 3, 4, 5]のまま
// evenNumbersは[2, 4]になります
1つのフィルタリング操作で最初に一つの条件(この例では年齢)でフィルタリングし、その結果からさらに異なる条件(職業)でフィルタリングを行う例
const profiles = [
{ name: "太郎", age: 40, profession: "エンジニア", city: "東京" },
{ name: "花子", age: 30, profession: "デザイナー", city: "大阪" },
{ name: "次郎", age: 35, profession: "エンジニア", city: "福岡" },
{ name: "美智子", age: 28, profession: "マーケター", city: "東京" },
{ name: "健", age: 22, profession: "エンジニア", city: "東京" }
];
const settings = {
ageThreshold: 25,
targetProfession: "エンジニア",
targetCity: "東京"
};
const resultArray = profiles.filter((profile) => {
// 年齢と職業の共通条件をチェック(25歳以上かつ職業がエンジニア)
if (profile.age >= settings.ageThreshold && profile.profession === settings.targetProfession) {
// さらに東京に住んでいる被地に絞る
return profile.city === settings.targetCity;
}
return false; // 上記の条件に合致しない場合は要素を含めない
});
// resultArrayは[{"name": "太郎", "age": 40, "profession": "エンジニア", "city": "東京"}]になります
find
メソッドは、与えられた条件を満たす最初の要素を返します。
const numbers = [1, 2, 3, 4, 5];
const firstEvenNumber = numbers.find(num => num % 2 === 0);
// firstEvenNumberは2になります
reduce
reduceは、配列の要素を単一の値にまとめるために使用されます。
各要素を処理し、累積された結果を返します。
足し算や最大値を求める時によく使います。
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
// sumは15になります
const people = [
{ age: 30, name: "John" },
{ age: 22, name: "Mike" },
{ age: 35, name: "Anna" },
{ age: 28, name: "John" },
{ age: 42, name: "Chris" }
];
const maxAge = people.reduce((max, person) => person.age > max ? person.age : max, people[0].age);
// maxAgeは42になります
Math.max()
最大値を求める時に使用します。
const numbers = [1, 2, 3, 4, 5];
const maxNumber = Math.max(...numbers);
// maxNumberは5になります
every&some
everyは、配列のすべての要素が与えられた条件を満たす場合にtrueを返します。
someは、配列のいずれかの要素が与えられた条件を満たす場合にtrueを返します。
const numbers = [1, 2, 3, 4, 5];
const checkThanZero = numbers.every(num => num > 0);
// checkThanZeroはtrueとなります
const checkThanThree = numbers.some(num => num > 3);
// checkThanZeroはtrueとなります
下記の場合、includes
を使用すると他の記述もできます。
const isHobbys = hobbys
.some(
item =>
item.hobby1 === 'gardening' ||
item.hobby2 === 'gardening' ||
item.hobby3 === 'gardening' ||
item.hobby1 === 'trip' ||
item.hobby2 === 'trip' ||
item.hobby3 === 'trip' ||
);
次のように記述できます。
const isHobbys = hobbys.some(item => {
// 各アイテムからhobbyプロパティを配列として抽出
const hobbies = [item.hobby1, item.hobby2, item.hobby3];
// 'gardening' または 'trip' を含むかどうかをチェック
return hobbies.includes('gardening') || hobbies.includes('trip');
});
型アサーションを使用した記述。
type HobbyType = {
hobby1: string;
hobby2: string;
hobby3: string;
};
const hobbyCheck = ['gardening', 'trip'];
const isHobbys = hobbys.some(item => {
for (let i = 1; i <= 3; i++) {
const hobby = item[`hobby${i}` as keyof HobbyType];
if (hobbyCheck.includes(hobby)) {
return true;
}
}
return false;
});
indexOf&lastIndexOf
indexOfメソッドは、指定された要素が配列内で最初に見つかったインデックスを返します。lastIndexOfメソッドは、指定された要素が配列内で最後に見つかったインデックスを返します。
const numbers = [1, 2, 3, 4, 5, 2];
console.log(numbers.indexOf(2)); // 1
console.log(numbers.lastIndexOf(2)); // 5
findIndex
与えられた条件を満たす最初の要素のインデックスを返します。
先ほどのindexOfとの違いは、findIndexはオブジェクトの特定のプロパティを条件として使用することができますが、indexOfはそれには対応していない点かなと思います。
const numbers = [1, 2, 3, 4, 5];
const firstIndex = numbers.findIndex(num => num % 2 === 0);
// firstIndexは1になります (2のインデックスは1です)
const array = [{id: 1}, {id: 2}, {id: 3}];
const index = array.findIndex(item => item.id === 3);
// indexは2になります
flatMap
各要素に対して与えられた関数を適用し、要素を1つ1つに分けた新しい配列を生成します。
const words = ['name', 'map'];
const characters = words.flatMap(word => word.split(''));
// charactersは['n', 'a', 'm', 'e', 'm', 'a', 'p']になります
reduce
配列の要素を1つにまとめるために使用されます。配列内の各要素に対して指定された関数を実行し、それぞれの呼び出しの結果を累積し、単一の値を返します。
足し算でよく使用します。
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
// sumは15になる (1 + 2 + 3 + 4 + 5)
const maxNumber = numbers.reduce(
(max, item) => (item > max ? item: max),
inputs[0]
);
sort
配列の要素を昇順、降順に並び替えることができます。
const numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3];
const sortedNumbers = array.sort((a, b) => a - b);
// sortedNumbersは[1, 1, 2, 3, 3, 4, 5, 5, 6, 9]になります
const sortedNumbers = numbers.sort((a, b) => b - a);
// sortedNumbersは[9, 6, 5, 5, 4, 3, 3, 2, 1, 1]になります
sortの使用例
指定された順序に従って配列を並び替える関数を作成することもできます。
const sortByOriginalOrder=(array, originalOrder) =>{
return array.sort((a, b) => {
return originalOrder.indexOf(a) - originalOrder.indexOf(b);
});
}
const originalOrder = ['北海道', '青森', '岩手'];
const shuffledPrefectures = ['北海道', '青森', '岩手', '北海道', '青森', '岩手', '北海道', '青森', '岩手'];
const sortedPrefectures = sortByOriginalOrder(shuffledPrefectures, originalOrder);
//sortedPrefecturesは['北海道', '北海道', '北海道', '青森', '青森', '青森', '岩手', '岩手', '岩手']になります
splice
配列を変更します。指定された位置から始まり、指定された数の要素を削除し、必要に応じて新しい要素を挿入します。
第一引数は追加・削除する位置
第ニ引数は削除する要素の数
第三引数以降は追加する要素
となっています。
let numbers = [1, 2, 3, 4, 5];
numbers.splice(2, 1); // インデックス2から1つの要素を削除します
// numbersは[1, 2, 4, 5]になります
numbers.splice(2, 0, 6, 7); // インデックス2に6と7を挿入します
// numbersは[1, 2, 6, 7, 4, 5]になります
concat
複数の配列を結合して新しい配列を作成します。
const numbers1 =[1, 2, 3];
const numbers2 = [4, 5, 6];
const newNumbers = numbers1.concat(numbers2);
// newNumbersは[1, 2, 3, 4, 5, 6]になります
slice
配列の一部を切り出して新しい配列を作成します。
const numbers = [1, 2, 3, 4, 5];
const = numbers.slice(1, 4); // インデックス1から4未満までの部分配列を取得
// slicedNumbersは[2, 3, 4]になります
join
引数として繋げる際の区切り文字列を指定できます。
const words = ['私は', '太郎', 'です'];
const sentence = words.join(''); // 空の文字列を指定して繋げる
// sentenceは"私は太郎です"になります
const sentence = words.join('、'); // コンマを挿入して繋げる
// sentenceは"私は、太郎、です"になります
オブジェクトのプロパティを変更する場合
元のオブジェクトprofile
のage
プロパティを直接変更しています。
これにより、元のオブジェクト自体が変更されます。
const profile = {
name: 'John',
age: 30
};
// ageプロパティを直接変更
profile.age = 35;
console.log(profile); // 変更後 { name: 'John', age: 35 }
元のオブジェクトを変更せずに、オブジェクトのプロパティを変更する場合はシャローコピーを使用してオブジェクトを複製し、それを変更してください。
const profile = {
name: 'John',
age: 30
};
// オブジェクトのシャローコピーを作成
const profileCopy = {...profile};
// シャローコピーのオブジェクトを変更
profileCopy.age = 35;
console.log(profile); // 元のオブジェクトは変更されない { name: 'John', age: 30 }
console.log(profileCopy); // シャローコピーのオブジェクトは変更された { name: 'John', age: 35 }
mapでシャローコピーを使用する場合
const people = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 35 }
];
// map関数を使用して特定の条件でageを更新
let updatedPeople = people.map(item => {
if (item.age === 30) { // `age`が30のオブジェクトを探す
return { ...item, age: 3 }; // スプレッド演算子を使ってitemのシャローコピーを作成し、ageを3に更新しています
} else {
return { ...item }; // 条件に一致しない場合、オブジェクトのコピーをそのまま返します
}
});
console.log(updatedPeople);
// [ { name: 'Alice', age: 25 }, { name: 'Bob', age: 3 }, { name: 'Charlie', age: 35 } ]
console.log(people);
// [ { name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }, { name: 'Charlie', age: 35 } ]
シャローコピーとは
シャローコピーは、元のオブジェクトのプロパティの値をコピーして、新しいオブジェクトを作成する方法です。
具体的には、スプレッド演算子{ ...object }
やObject.assign()
メソッドを使用して、オブジェクトの浅いコピーを作成します。
終わりに
何かありましたらお気軽にコメント等いただけると助かります。
ここまでお読みいただきありがとうございます🎉
Discussion