tl;dr
以下のようなデータがあるとき、各値に対して* 5
の計算をした結果を出したい。
const flavor = {
umami: 0.1,
amasa: 0.6,
nigasa: 0.4,
}
しかし、キーごとに処理を書くと、キーがものすごく多い場合は困る。
flavor.umami * 5
flavor.amasa * 5
flavor.nigasa * 5
Objectのentries
とfromEntries
で解決する
const timeFive = Object.fromEntries(
Object.entries(flavor)
.map(([k, v]) => [k, v * 5])
)
メソッド紹介
以下のようなデータの出力結果を見ながらメソッドを理解してみましょう
const person = {
name: "田中",
age: 30,
city: "東京"
};
entries
オブジェクトから、キーとバリューをとってきて配列にしてくれる
console.log(Object.entries(person));
// [["name", "田中"], ["age", 30], ["city", "東京"]]
fromEntries
entries
で出力する配列をオブジェクト化してくれる
const entries = [["name", "田中"], ["age", 30], ["city", "東京"]];
console.log(Object.fromEntries(entries));
// { name: "田中", age: 30, city: "東京" }
応用
このメソッドを利用すると以下のようなこともできる!
ケース1: オブジェクトの値を一括変換
const prices = { apple: 100, banana: 200, orange: 150 };
const discountedPrices = Object.fromEntries(
Object.entries(prices)
.map(([item, price]) => [item, price * 0.9])
);
// { apple: 90, banana: 180, orange: 135 }
ケース2: キーと値を入れ替える
const colors = { red: "#FF0000", blue: "#0000FF", green: "#00FF00" };
const reversed = Object.fromEntries(
Object.entries(colors)
.map(([name, code]) => [code, name])
);
// { "#FF0000": "red", "#0000FF": "blue", "#00FF00": "green" }
ケース3: 条件に基づくフィルタリング
const scores = { math: 85, english: 65, science: 90 };
const highScores = Object.fromEntries(
Object.entries(scores)
.filter(([_, score]) => score >= 80)
);
// { math: 85, science: 90 }
ケース4: ネストされたオブジェクトの処理
const users = {
user1: { name: "田中", age: 30 },
user2: { name: "佐藤", age: 25 }
};
const processedUsers = Object.fromEntries(
Object.entries(users).map(([id, data]) => [
id,
{ ...data, ageGroup: data.age >= 30 ? "シニア" : "ジュニア" }
])
);
// オブジェクトのバリデーション
const validateUserData = obj => Object.fromEntries(
Object.entries(obj).map(([key, value]) => [
key,
typeof value === "string" ? value.trim() : value
])
);
TypeScriptとの相性
1. 型安全な変換処理
asでキーと値の型を明示的に定義することができる
interface Flavor {
umami: number;
amasa: number;
nigasa: number;
}
const flavor: Flavor = {
umami: 0.1,
amasa: 0.6,
nigasa: 0.4,
};
// 型を保持したまま変換
const timeFive = Object.fromEntries(
Object.entries(flavor).map(([k, v]) => [k, v * 5])
) as Flavor;
2. ユーティリティ型との組み合わせ
Record型を使用すると、動的なキーを持つオブジェクトを定義できる。
type ScoreRecord = Record<string, number>;
const scores: ScoreRecord = {
math: 85,
english: 65,
science: 90,
};
// 型安全な値の変換
const normalizedScores = Object.fromEntries(
Object.entries(scores).map(([subject, score]) => [
subject,
Math.min(100, score)
])
) as ScoreRecord;
3. カスタム型ガード付きフィルタリング
ほしい型のデータだけを絞り込むこともできる!
interface UserData {
name: string;
age: number;
email?: string;
}
type UserRecord = Record<string, UserData>;
const users: UserRecord = {
user1: { name: "田中", age: 30 },
user2: { name: "佐藤", age: 25, email: "sato@example.com" }
};
// メールアドレスを持つユーザーのみをフィルタリング
const hasEmail = (user: UserData): boolean => typeof user.email === "string";
const usersWithEmail = Object.fromEntries(
Object.entries(users)
.filter(([_, userData]) => hasEmail(userData))
) as UserRecord;
4. 型変換に伴う処理
安全に型変換をするときに使うこともできる。
interface RawUserData {
name: string;
age: string; // 最初は文字列として受け取る
}
interface ProcessedUserData {
name: string;
age: number; // 数値に変換
}
// 型変換を行うユーティリティ関数
const processUserData = (data: RawUserData): ProcessedUserData => ({
name: data.name,
age: parseInt(data.age, 10)
});
const rawUsers: Record<string, RawUserData> = {
user1: { name: "田中", age: "30" },
user2: { name: "佐藤", age: "25" }
};
// 型変換を適用
const processedUsers = Object.fromEntries(
Object.entries(rawUsers)
.map(([id, data]) => [id, processUserData(data)])
) as Record<string, ProcessedUserData>;
おわりに
最初は短い書き方がしたくて調べていたけど、使い道が多そうでもっとプログラムに取り入れていきたいと思った。
パフォーマンス的にはObject.keys
を使った方がいい場面も多そうなのでそのへんについてもっと調べていきたいな。