mapメソッドで連想配列を操作!JavaScript初心者ガイド

JavaScriptのmapメソッドを使った連想配列操作について初心者向けに完全解説。基本的な使い方から実践的な活用例まで詳しく説明します。

Learning Next 運営
32 分で読めます

mapメソッドで連想配列を操作!JavaScript初心者ガイド

みなさん、JavaScriptで配列の中身を一気に変換したいと思ったことはありませんか?

「配列のデータを全部まとめて変更したい」 「mapメソッドって何?どうやって使うの?」

こんな風に思ったことはありませんか?

この記事では、JavaScriptのmapメソッドを使った連想配列操作について、初心者にも分かりやすく解説します。 基本的な使い方から実践的な活用例まで、配列を効率的に変換する技術を一緒にマスターしていきましょう!

mapメソッドって何のこと?

mapメソッドは、配列の各要素に対して関数を実行し、その結果で新しい配列を作成するJavaScriptの組み込みメソッドです。

簡単に言うと、「配列の中身を一つずつ変換して、新しい配列を作る」機能なんです。 元の配列は変更されず、変換結果として新しい配列が返されるため、安全で予測可能な処理を行えます。

イメージとしては、工場のベルトコンベアのような感じです。 原材料(元の配列)がベルトコンベアを流れて、加工機械(map関数)で変換されて、完成品(新しい配列)として出てくる感じですね。

連想配列(オブジェクトの配列)

連想配列とは、一般的にオブジェクトを要素とする配列のことを指します。

// 連想配列の例
let users = [
{ id: 1, name: '田中', age: 25 },
{ id: 2, name: '佐藤', age: 30 },
{ id: 3, name: '山田', age: 28 }
];

このような構造のデータを見たことがありますよね。

配列の中に、オブジェクト({}で囲まれたデータ)が入っています。 このような構造のデータをmapメソッドで効率的に操作できるんです。

mapメソッドの基本構文

基本的な使い方

mapメソッドの基本的な構文は以下の通りです。

// 基本構文
let newArray = originalArray.map(function(element, index, array) {
// 変換処理
return newElement;
});
// アロー関数を使った短縮記法
let newArray = originalArray.map((element, index) => {
return newElement;
});
// さらに短縮
let newArray = originalArray.map(element => newElement);

この構文について詳しく説明しますね。

originalArray.map()で元の配列に対してmapメソッドを呼び出します。

let newArray = originalArray.map(function(element, index, array) {

関数の引数は以下の通りです。

  • element: 現在処理している要素
  • index: 現在の要素のインデックス(位置)
  • array: 元の配列全体
return newElement;

関数は、変換後の要素を返します。 この戻り値が新しい配列の要素になります。

最もシンプルな例

数値の配列を2倍にする基本例を見てみましょう。

let numbers = [1, 2, 3, 4, 5];
let doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(numbers); // [1, 2, 3, 4, 5](元の配列は変更されない)

この例では、各数値を2倍にしています。

num => num * 2の部分で、各要素を2倍にする処理を書いています。

重要なのは、元の配列numbersは変更されていないことです。 doubledは全く新しい配列として作成されます。

連想配列での基本操作

オブジェクトの特定プロパティを抽出

連想配列から特定のプロパティのみを取り出す例です。

let users = [
{ id: 1, name: '田中', age: 25, city: '東京' },
{ id: 2, name: '佐藤', age: 30, city: '大阪' },
{ id: 3, name: '山田', age: 28, city: '名古屋' }
];
// 名前だけを抽出
let names = users.map(user => user.name);
console.log(names); // ['田中', '佐藤', '山田']
// IDと名前を抽出
let profiles = users.map(user => ({
id: user.id,
name: user.name
}));
console.log(profiles);
// [{ id: 1, name: '田中' }, { id: 2, name: '佐藤' }, { id: 3, name: '山田' }]

この例では、ユーザー情報から必要な部分だけを取り出しています。

user => user.nameで名前だけを抽出します。

let names = users.map(user => user.name);

複数のプロパティを抽出したい場合は、新しいオブジェクトを作成します。

let profiles = users.map(user => ({
id: user.id,
name: user.name
}));

({})の括弧は、オブジェクトをreturnするために必要です。

プロパティの値を変換

既存のプロパティの値を変換する例です。

let products = [
{ name: '商品A', price: 1000 },
{ name: '商品B', price: 2000 },
{ name: '商品C', price: 1500 }
];
// 価格を税込み価格に変換
let productsWithTax = products.map(product => ({
...product,
price: Math.round(product.price * 1.1)
}));
console.log(productsWithTax);
// [
// { name: '商品A', price: 1100 },
// { name: '商品B', price: 2200 },
// { name: '商品C', price: 1650 }
// ]

この例では、商品価格を税込み価格に変換しています。

...productでスプレッド演算子を使って、元のオブジェクトのプロパティをすべてコピーします。

...product,

その後、特定のプロパティ(price)だけを上書きします。

price: Math.round(product.price * 1.1)

Math.roundで小数点以下を四捨五入しています。

新しいプロパティの追加

既存のオブジェクトに新しいプロパティを追加する例です。

let students = [
{ name: '田中', math: 80, english: 75 },
{ name: '佐藤', math: 95, english: 85 },
{ name: '山田', math: 70, english: 90 }
];
// 平均点を追加
let studentsWithAverage = students.map(student => ({
...student,
average: Math.round((student.math + student.english) / 2)
}));
console.log(studentsWithAverage);
// [
// { name: '田中', math: 80, english: 75, average: 78 },
// { name: '佐藤', math: 95, english: 85, average: 90 },
// { name: '山田', math: 70, english: 90, average: 80 }
// ]

この例では、数学と英語の平均点を計算して、新しいプロパティとして追加しています。

既存のプロパティはそのまま残して、新しいプロパティaverageを追加します。

average: Math.round((student.math + student.english) / 2)

数学と英語の点数を足して2で割り、Math.roundで四捨五入しています。

実践的な使用例

APIレスポンスの変換

APIから取得したデータを画面表示用に変換する例です。

// APIから取得したユーザーデータ(模擬)
let apiUsers = [
{
user_id: 1,
first_name: '太郎',
last_name: '田中',
birth_date: '1998-05-15',
is_active: true
},
{
user_id: 2,
first_name: '花子',
last_name: '佐藤',
birth_date: '1995-12-03',
is_active: false
}
];
// 画面表示用に変換
let displayUsers = apiUsers.map(user => ({
id: user.user_id,
fullName: user.last_name + ' ' + user.first_name,
age: calculateAge(user.birth_date),
status: user.is_active ? 'アクティブ' : '非アクティブ'
}));
function calculateAge(birthDate) {
let today = new Date();
let birth = new Date(birthDate);
let age = today.getFullYear() - birth.getFullYear();
if (today.getMonth() < birth.getMonth() ||
(today.getMonth() === birth.getMonth() && today.getDate() < birth.getDate())) {
age--;
}
return age;
}
console.log(displayUsers);
// [
// { id: 1, fullName: '田中 太郎', age: 26, status: 'アクティブ' },
// { id: 2, fullName: '佐藤 花子', age: 29, status: '非アクティブ' }
// ]

この例では、APIのデータ形式を画面表示用に変換しています。

APIではuser_idfirst_namelast_nameのように分かれているデータを、画面で使いやすい形に変換します。

id: user.user_id,
fullName: user.last_name + ' ' + user.first_name,

calculateAge関数で年齢を計算します。

age: calculateAge(user.birth_date),

三項演算子でステータスを日本語に変換します。

status: user.is_active ? 'アクティブ' : '非アクティブ'

ショッピングカートの計算

ショッピングカートの商品データを変換する例です。

let cartItems = [
{ productId: 1, name: 'ノートPC', price: 80000, quantity: 1 },
{ productId: 2, name: 'マウス', price: 2000, quantity: 2 },
{ productId: 3, name: 'キーボード', price: 5000, quantity: 1 }
];
// 各商品の小計を計算
let itemsWithSubtotal = cartItems.map(item => ({
...item,
subtotal: item.price * item.quantity,
formattedPrice: formatCurrency(item.price),
formattedSubtotal: formatCurrency(item.price * item.quantity)
}));
function formatCurrency(amount) {
return '¥' + amount.toLocaleString();
}
console.log(itemsWithSubtotal);
// [
// { productId: 1, name: 'ノートPC', price: 80000, quantity: 1,
// subtotal: 80000, formattedPrice: '¥80,000', formattedSubtotal: '¥80,000' },
// { productId: 2, name: 'マウス', price: 2000, quantity: 2,
// subtotal: 4000, formattedPrice: '¥2,000', formattedSubtotal: '¥4,000' },
// { productId: 3, name: 'キーボード', price: 5000, quantity: 1,
// subtotal: 5000, formattedPrice: '¥5,000', formattedSubtotal: '¥5,000' }
// ]

この例では、ショッピングカートの各商品に小計を計算して追加しています。

小計の計算は価格×数量で行います。

subtotal: item.price * item.quantity,

formatCurrency関数で金額を見やすい形式に変換します。

function formatCurrency(amount) {
return '¥' + amount.toLocaleString();
}

toLocaleString()で3桁区切りの数値表示にします。

HTML要素の生成

配列データからHTML要素を生成する例です。

let menuItems = [
{ id: 1, title: 'ホーム', url: '/', active: true },
{ id: 2, title: 'サービス', url: '/services', active: false },
{ id: 3, title: 'お問い合わせ', url: '/contact', active: false }
];
// HTML要素を生成
let menuElements = menuItems.map(item => {
let li = document.createElement('li');
li.className = item.active ? 'nav-item active' : 'nav-item';
let a = document.createElement('a');
a.href = item.url;
a.textContent = item.title;
li.appendChild(a);
return li;
});
// またはHTML文字列として生成
let menuHTML = menuItems.map(item =>
`<li class="nav-item ${item.active ? 'active' : ''}">
<a href="${item.url}">${item.title}</a>
</li>`
).join('');
console.log(menuHTML);

この例では、メニューデータからHTML要素を生成しています。

DOM要素を作成する方法では、document.createElementを使います。

let li = document.createElement('li');
li.className = item.active ? 'nav-item active' : 'nav-item';

HTML文字列として生成する方法では、テンプレートリテラルを使います。

`<li class="nav-item ${item.active ? 'active' : ''}">
<a href="${item.url}">${item.title}</a>
</li>`

最後に.join('')で配列の要素を結合して、一つの文字列にします。

複雑な変換処理

ネストしたオブジェクトの処理

複雑な構造のオブジェクトを変換する例です。

let orders = [
{
orderId: 1,
customer: { name: '田中太郎', email: 'tanaka@example.com' },
items: [
{ productId: 1, name: '商品A', price: 1000, quantity: 2 },
{ productId: 2, name: '商品B', price: 500, quantity: 1 }
],
orderDate: '2024-01-15'
},
{
orderId: 2,
customer: { name: '佐藤花子', email: 'sato@example.com' },
items: [
{ productId: 3, name: '商品C', price: 2000, quantity: 1 }
],
orderDate: '2024-01-16'
}
];
// 注文サマリーを生成
let orderSummaries = orders.map(order => {
let totalAmount = order.items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
let itemCount = order.items.reduce((count, item) => count + item.quantity, 0);
return {
orderId: order.orderId,
customerName: order.customer.name,
itemCount: itemCount,
totalAmount: totalAmount,
formattedTotal: '¥' + totalAmount.toLocaleString(),
orderDate: formatDate(order.orderDate)
};
});
function formatDate(dateString) {
let date = new Date(dateString);
return date.getFullYear() + '年' +
(date.getMonth() + 1) + '月' +
date.getDate() + '日';
}
console.log(orderSummaries);

この例では、注文データから注文サマリーを作成しています。

まず、注文に含まれる商品の合計金額を計算します。

let totalAmount = order.items.reduce((sum, item) => sum + (item.price * item.quantity), 0);

reduceメソッドで、全ての商品の価格×数量を合計しています。

次に、商品の総数量を計算します。

let itemCount = order.items.reduce((count, item) => count + item.quantity, 0);

最後に、必要な情報だけを抽出して新しいオブジェクトを作成します。

条件に応じた変換

条件に基づいて異なる変換を適用する例です。

let employees = [
{ id: 1, name: '田中', department: 'engineering', experience: 5 },
{ id: 2, name: '佐藤', department: 'sales', experience: 3 },
{ id: 3, name: '山田', department: 'engineering', experience: 8 },
{ id: 4, name: '鈴木', department: 'marketing', experience: 2 }
];
// 部署と経験年数に基づいて給与を計算
let employeesWithSalary = employees.map(employee => {
let baseSalary;
// 部署による基本給
switch (employee.department) {
case 'engineering':
baseSalary = 500000;
break;
case 'sales':
baseSalary = 400000;
break;
case 'marketing':
baseSalary = 450000;
break;
default:
baseSalary = 350000;
}
// 経験年数による加算
let experienceBonus = employee.experience * 20000;
let totalSalary = baseSalary + experienceBonus;
return {
...employee,
baseSalary: baseSalary,
experienceBonus: experienceBonus,
totalSalary: totalSalary,
level: employee.experience >= 5 ? 'シニア' : 'ジュニア'
};
});
console.log(employeesWithSalary);

この例では、部署と経験年数に基づいて給与を計算しています。

switch文で部署に応じた基本給を設定します。

switch (employee.department) {
case 'engineering':
baseSalary = 500000;
break;
case 'sales':
baseSalary = 400000;
break;

経験年数ボーナスを計算します。

let experienceBonus = employee.experience * 20000;

最後に、経験年数に基づいてレベルを判定します。

level: employee.experience >= 5 ? 'シニア' : 'ジュニア'

mapメソッドと他のメソッドの組み合わせ

filterとmapの連携

条件に合う要素のみを抽出してから変換する例です。

let products = [
{ id: 1, name: '商品A', price: 1000, category: 'electronics', inStock: true },
{ id: 2, name: '商品B', price: 500, category: 'clothing', inStock: false },
{ id: 3, name: '商品C', price: 2000, category: 'electronics', inStock: true },
{ id: 4, name: '商品D', price: 800, category: 'books', inStock: true }
];
// 在庫ありで1000円以上の商品に割引を適用
let discountedProducts = products
.filter(product => product.inStock && product.price >= 1000)
.map(product => ({
...product,
originalPrice: product.price,
discountedPrice: Math.round(product.price * 0.9),
discount: '10%OFF'
}));
console.log(discountedProducts);

この例では、filtermapを組み合わせています。

まず、filterで条件に合う商品だけを抽出します。

.filter(product => product.inStock && product.price >= 1000)

在庫があり、かつ価格が1000円以上の商品だけが残ります。

次に、mapで割引価格を追加します。

.map(product => ({
...product,
originalPrice: product.price,
discountedPrice: Math.round(product.price * 0.9),
discount: '10%OFF'
}));

このように、メソッドを連続して使うことをメソッドチェーンと呼びます。

mapとreduceの連携

変換後にデータを集計する例です。

let sales = [
{ date: '2024-01-01', amount: 10000, region: '東京' },
{ date: '2024-01-02', amount: 15000, region: '大阪' },
{ date: '2024-01-03', amount: 8000, region: '東京' },
{ date: '2024-01-04', amount: 12000, region: '福岡' }
];
// 地域別売上を計算
let regionalSales = sales
.map(sale => ({
...sale,
month: sale.date.substring(0, 7) // YYYY-MM形式
}))
.reduce((acc, sale) => {
if (!acc[sale.region]) {
acc[sale.region] = 0;
}
acc[sale.region] += sale.amount;
return acc;
}, {});
console.log(regionalSales); // { '東京': 18000, '大阪': 15000, '福岡': 12000 }

この例では、mapでデータを変換してからreduceで集計しています。

mapで月の情報を追加します。

.map(sale => ({
...sale,
month: sale.date.substring(0, 7) // YYYY-MM形式
}))

reduceで地域別の売上を集計します。

.reduce((acc, sale) => {
if (!acc[sale.region]) {
acc[sale.region] = 0;
}
acc[sale.region] += sale.amount;
return acc;
}, {});

accは累積値で、各地域の売上を格納します。

よくある間違いとその対処法

元の配列を変更してしまう間違い

mapメソッドは新しい配列を返すことを理解しましょう。

let products = [
{ id: 1, name: '商品A', price: 1000 }
];
// 間違い:元のオブジェクトを変更
let badExample = products.map(product => {
product.price = product.price * 1.1; // 元のオブジェクトが変更される
return product;
});
// 正しい:新しいオブジェクトを作成
let goodExample = products.map(product => ({
...product,
price: product.price * 1.1
}));
console.log(products[0].price); // badExampleの場合は1100、goodExampleの場合は1000

この間違いはとても重要なポイントです。

間違った例では、元のオブジェクトのpriceプロパティを直接変更しています。

product.price = product.price * 1.1; // 元のオブジェクトが変更される

正しい例では、スプレッド演算子で新しいオブジェクトを作成しています。

({
...product,
price: product.price * 1.1
})

この違いにより、元の配列productsが保持されるかどうかが決まります。

戻り値を忘れる間違い

mapメソッドでは、関数から値を返すことが重要です。

let numbers = [1, 2, 3];
// 間違い:何も返さない
let badResult = numbers.map(num => {
console.log(num * 2); // 出力はされるが戻り値がない
});
console.log(badResult); // [undefined, undefined, undefined]
// 正しい:値を返す
let goodResult = numbers.map(num => {
console.log(num * 2);
return num * 2;
});
console.log(goodResult); // [2, 4, 6]

間違った例では、return文がありません。

console.log(num * 2); // 出力はされるが戻り値がない

この場合、関数の戻り値はundefinedになってしまいます。

正しい例では、return文で値を返しています。

return num * 2;

mapメソッドでは、戻り値が新しい配列の要素になるので、returnを忘れないようにしましょう。

まとめ

JavaScriptのmapメソッドは、連想配列を効率的に変換するための強力で重要なツールです。

重要なポイントをまとめました。

  • mapメソッドは元の配列を変更せず、新しい配列を作成する
  • 連想配列の各オブジェクトを任意の形に変換できる
  • APIデータの変換、計算処理、HTML生成など幅広い用途で活用できる
  • filter、reduceなど他のメソッドとの組み合わせで強力な処理が可能
  • 元のデータを変更しない、戻り値を忘れないことが重要

mapメソッドを適切に使用することで、データ変換処理が簡潔で読みやすくなり、バグの少ないコードを書くことができます!

特に、現代のWebアプリケーション開発において、mapメソッドは欠かせない技術となっています。

これらの基礎技術をマスターして、より効率的なJavaScriptプログラムを作成してみませんか? きっと、データ処理がもっと楽しくなりますよ!

関連記事