Lodash の filter・find 活用術:データ抽出の極意

JavaScript の配列操作で、データの抽出や検索を行う際に、多くの開発者が直面する課題があります。ネイティブの JavaScript メソッドだけでは複雑な条件での検索が困難だったり、パフォーマンスの問題が発生したりすることがあります。
そんな時、Lodash のfilter
とfind
関数が強力な武器となります。これらの関数を使いこなすことで、効率的で読みやすいコードを書くことができ、開発効率が大幅に向上します。
この記事では、Lodash のfilter
とfind
関数の基本から応用まで、実践的な活用パターンを詳しく解説していきます。初心者の方でも理解しやすいように、段階的に説明し、実際のプロジェクトで使えるテクニックを多数紹介します。
filter 関数の基本と応用
filter 関数の基本構文
Lodash のfilter
関数は、配列から条件に一致する要素を抽出するための関数です。ネイティブのArray.filter()
と似ていますが、より柔軟で強力な機能を提供します。
まずは基本的な構文から確認してみましょう。
javascript// 基本的なfilter関数の使い方
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 偶数だけを抽出
const evenNumbers = _.filter(
numbers,
(num) => num % 2 === 0
);
console.log(evenNumbers); // [2, 4, 6, 8, 10]
// 5より大きい数を抽出
const largeNumbers = _.filter(numbers, (num) => num > 5);
console.log(largeNumbers); // [6, 7, 8, 9, 10]
filter 関数の第 1 引数は対象の配列、第 2 引数は条件を判定する関数です。この関数は各要素に対して実行され、true
を返した要素が結果に含まれます。
配列オブジェクトの条件抽出
実際の開発では、オブジェクトの配列を扱うことが多いです。filter 関数を使うことで、オブジェクトの特定のプロパティに基づいて条件抽出ができます。
javascript// ユーザーオブジェクトの配列
const users = [
{ id: 1, name: '田中太郎', age: 25, isActive: true },
{ id: 2, name: '佐藤花子', age: 30, isActive: false },
{ id: 3, name: '鈴木一郎', age: 22, isActive: true },
{ id: 4, name: '高橋美咲', age: 28, isActive: true },
{ id: 5, name: '伊藤健太', age: 35, isActive: false },
];
// アクティブなユーザーのみを抽出
const activeUsers = _.filter(
users,
(user) => user.isActive
);
console.log(activeUsers);
// [
// { id: 1, name: '田中太郎', age: 25, isActive: true },
// { id: 3, name: '鈴木一郎', age: 22, isActive: true },
// { id: 4, name: '高橋美咲', age: 28, isActive: true }
// ]
// 25歳以上のユーザーを抽出
const adultUsers = _.filter(
users,
(user) => user.age >= 25
);
console.log(adultUsers);
// [
// { id: 1, name: '田中太郎', age: 25, isActive: true },
// { id: 2, name: '佐藤花子', age: 30, isActive: false },
// { id: 4, name: '高橋美咲', age: 28, isActive: true },
// { id: 5, name: '伊藤健太', age: 35, isActive: false }
// ]
複数条件での絞り込み
複数の条件を組み合わせて、より精密な抽出を行うことができます。論理演算子を活用することで、複雑な条件も簡単に表現できます。
javascript// 複数条件での絞り込み
const users = [
{
id: 1,
name: '田中太郎',
age: 25,
isActive: true,
role: 'admin',
},
{
id: 2,
name: '佐藤花子',
age: 30,
isActive: false,
role: 'user',
},
{
id: 3,
name: '鈴木一郎',
age: 22,
isActive: true,
role: 'user',
},
{
id: 4,
name: '高橋美咲',
age: 28,
isActive: true,
role: 'moderator',
},
{
id: 5,
name: '伊藤健太',
age: 35,
isActive: false,
role: 'user',
},
];
// アクティブで25歳以上のユーザー
const activeAdultUsers = _.filter(
users,
(user) => user.isActive && user.age >= 25
);
console.log(activeAdultUsers);
// [
// { id: 1, name: '田中太郎', age: 25, isActive: true, role: 'admin' },
// { id: 4, name: '高橋美咲', age: 28, isActive: true, role: 'moderator' }
// ]
// 管理者またはモデレーターで、アクティブなユーザー
const adminOrModerator = _.filter(
users,
(user) =>
user.isActive &&
(user.role === 'admin' || user.role === 'moderator')
);
console.log(adminOrModerator);
// [
// { id: 1, name: '田中太郎', age: 25, isActive: true, role: 'admin' },
// { id: 4, name: '高橋美咲', age: 28, isActive: true, role: 'moderator' }
// ]
ネストしたオブジェクトの検索
オブジェクトの中にさらにオブジェクトがネストしている場合でも、filter 関数を使って効率的に検索できます。
javascript// ネストしたオブジェクトの配列
const products = [
{
id: 1,
name: 'スマートフォン',
category: {
id: 1,
name: '電子機器',
parent: {
id: 1,
name: '家電',
},
},
price: 80000,
inStock: true,
},
{
id: 2,
name: 'ノートPC',
category: {
id: 1,
name: '電子機器',
parent: {
id: 1,
name: '家電',
},
},
price: 120000,
inStock: false,
},
{
id: 3,
name: 'コーヒー豆',
category: {
id: 2,
name: '食品',
parent: {
id: 2,
name: '日用品',
},
},
price: 1500,
inStock: true,
},
];
// カテゴリ名で絞り込み
const electronics = _.filter(
products,
(product) => product.category.name === '電子機器'
);
console.log(electronics);
// [スマートフォン, ノートPC]
// 在庫があり、価格が5000円以下の商品
const affordableInStock = _.filter(
products,
(product) => product.inStock && product.price <= 5000
);
console.log(affordableInStock);
// [コーヒー豆]
find 関数の基本と応用
find 関数の基本構文
find
関数は、配列から条件に一致する最初の要素を取得する関数です。filter
が条件に一致するすべての要素を返すのに対し、find
は最初の 1 つだけを返します。
javascript// 基本的なfind関数の使い方
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 5より大きい最初の数を取得
const firstLargeNumber = _.find(numbers, (num) => num > 5);
console.log(firstLargeNumber); // 6
// 偶数の中で最初のものを取得
const firstEvenNumber = _.find(
numbers,
(num) => num % 2 === 0
);
console.log(firstEvenNumber); // 2
find 関数は、条件に一致する要素が見つからない場合はundefined
を返します。この特性を活用して、存在チェックにも使えます。
最初の一致要素の取得
オブジェクトの配列から、特定の条件に一致する最初の要素を効率的に取得できます。
javascript// ユーザー配列から特定のユーザーを検索
const users = [
{
id: 1,
name: '田中太郎',
email: 'tanaka@example.com',
isActive: true,
},
{
id: 2,
name: '佐藤花子',
email: 'sato@example.com',
isActive: false,
},
{
id: 3,
name: '鈴木一郎',
email: 'suzuki@example.com',
isActive: true,
},
{
id: 4,
name: '高橋美咲',
email: 'takahashi@example.com',
isActive: true,
},
];
// IDでユーザーを検索
const userById = _.find(users, (user) => user.id === 3);
console.log(userById);
// { id: 3, name: '鈴木一郎', email: 'suzuki@example.com', isActive: true }
// メールアドレスでユーザーを検索
const userByEmail = _.find(
users,
(user) => user.email === 'sato@example.com'
);
console.log(userByEmail);
// { id: 2, name: '佐藤花子', email: 'sato@example.com', isActive: false }
// 存在しないユーザーの検索
const nonExistentUser = _.find(
users,
(user) => user.id === 999
);
console.log(nonExistentUser); // undefined
複雑な条件での検索
複数の条件を組み合わせて、より精密な検索を行うことができます。
javascript// 複雑な条件での検索
const orders = [
{
id: 1,
customerId: 101,
status: 'pending',
amount: 5000,
createdAt: '2024-01-15',
},
{
id: 2,
customerId: 102,
status: 'completed',
amount: 8000,
createdAt: '2024-01-16',
},
{
id: 3,
customerId: 101,
status: 'pending',
amount: 3000,
createdAt: '2024-01-17',
},
{
id: 4,
customerId: 103,
status: 'cancelled',
amount: 12000,
createdAt: '2024-01-18',
},
{
id: 5,
customerId: 101,
status: 'completed',
amount: 6000,
createdAt: '2024-01-19',
},
];
// 特定の顧客の最初の保留中注文
const firstPendingOrder = _.find(
orders,
(order) =>
order.customerId === 101 && order.status === 'pending'
);
console.log(firstPendingOrder);
// { id: 1, customerId: 101, status: 'pending', amount: 5000, createdAt: '2024-01-15' }
// 5000円以上の最初の完了注文
const firstLargeCompletedOrder = _.find(
orders,
(order) =>
order.status === 'completed' && order.amount >= 5000
);
console.log(firstLargeCompletedOrder);
// { id: 2, customerId: 102, status: 'completed', amount: 8000, createdAt: '2024-01-16' }
パフォーマンスの最適化
find 関数は最初の一致要素を見つけた時点で処理を停止するため、配列の先頭に条件に一致する要素がある場合に特に効率的です。
javascript// パフォーマンスを考慮した検索順序
const largeDataset = Array.from(
{ length: 10000 },
(_, i) => ({
id: i,
name: `Item ${i}`,
priority:
i % 3 === 0 ? 'high' : i % 3 === 1 ? 'medium' : 'low',
})
);
// 高優先度のアイテムを最初に検索(効率的)
const highPriorityItem = _.find(
largeDataset,
(item) => item.priority === 'high'
);
console.log(highPriorityItem); // { id: 0, name: 'Item 0', priority: 'high' }
// 低優先度のアイテムを検索(非効率)
const lowPriorityItem = _.find(
largeDataset,
(item) => item.priority === 'low'
);
console.log(lowPriorityItem); // { id: 2, name: 'Item 2', priority: 'low' }
実践的な活用パターン
ユーザー管理システムでの活用
実際のユーザー管理システムでは、様々な条件でのユーザー検索や抽出が必要になります。
javascript// ユーザー管理システムでの実装例
class UserManager {
constructor() {
this.users = [
{
id: 1,
name: '田中太郎',
email: 'tanaka@example.com',
role: 'admin',
isActive: true,
lastLogin: '2024-01-20',
},
{
id: 2,
name: '佐藤花子',
email: 'sato@example.com',
role: 'user',
isActive: true,
lastLogin: '2024-01-19',
},
{
id: 3,
name: '鈴木一郎',
email: 'suzuki@example.com',
role: 'moderator',
isActive: false,
lastLogin: '2024-01-15',
},
{
id: 4,
name: '高橋美咲',
email: 'takahashi@example.com',
role: 'user',
isActive: true,
lastLogin: '2024-01-18',
},
{
id: 5,
name: '伊藤健太',
email: 'ito@example.com',
role: 'user',
isActive: false,
lastLogin: '2024-01-10',
},
];
}
// アクティブなユーザーを取得
getActiveUsers() {
return _.filter(this.users, (user) => user.isActive);
}
// 特定のロールのユーザーを取得
getUsersByRole(role) {
return _.filter(
this.users,
(user) => user.role === role
);
}
// ユーザーIDでユーザーを検索
findUserById(id) {
return _.find(this.users, (user) => user.id === id);
}
// メールアドレスでユーザーを検索
findUserByEmail(email) {
return _.find(
this.users,
(user) => user.email === email
);
}
// 最近ログインしたユーザーを取得
getRecentlyActiveUsers(days = 7) {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - days);
return _.filter(this.users, (user) => {
const lastLogin = new Date(user.lastLogin);
return lastLogin >= cutoffDate;
});
}
}
// 使用例
const userManager = new UserManager();
console.log(
'アクティブユーザー:',
userManager.getActiveUsers().length
);
console.log('管理者:', userManager.getUsersByRole('admin'));
console.log('特定ユーザー:', userManager.findUserById(3));
商品検索機能での実装
EC サイトでの商品検索機能では、複数の条件を組み合わせた高度な検索が必要になります。
javascript// 商品検索システムでの実装例
class ProductSearch {
constructor() {
this.products = [
{
id: 1,
name: 'iPhone 15',
category: 'スマートフォン',
brand: 'Apple',
price: 120000,
inStock: true,
rating: 4.5,
},
{
id: 2,
name: 'Galaxy S24',
category: 'スマートフォン',
brand: 'Samsung',
price: 100000,
inStock: true,
rating: 4.3,
},
{
id: 3,
name: 'MacBook Pro',
category: 'ノートPC',
brand: 'Apple',
price: 250000,
inStock: false,
rating: 4.7,
},
{
id: 4,
name: 'ThinkPad X1',
category: 'ノートPC',
brand: 'Lenovo',
price: 180000,
inStock: true,
rating: 4.2,
},
{
id: 5,
name: 'AirPods Pro',
category: 'イヤホン',
brand: 'Apple',
price: 35000,
inStock: true,
rating: 4.6,
},
];
}
// 基本的な検索
searchByKeyword(keyword) {
return _.filter(this.products, (product) =>
product.name
.toLowerCase()
.includes(keyword.toLowerCase())
);
}
// 価格範囲での検索
searchByPriceRange(minPrice, maxPrice) {
return _.filter(
this.products,
(product) =>
product.price >= minPrice &&
product.price <= maxPrice
);
}
// カテゴリとブランドでの絞り込み
searchByCategoryAndBrand(category, brand) {
return _.filter(
this.products,
(product) =>
product.category === category &&
product.brand === brand
);
}
// 在庫あり商品のみ
getInStockProducts() {
return _.filter(
this.products,
(product) => product.inStock
);
}
// 高評価商品(4.5以上)
getHighRatedProducts() {
return _.filter(
this.products,
(product) => product.rating >= 4.5
);
}
// 複合条件での検索
advancedSearch(criteria) {
return _.filter(this.products, (product) => {
// キーワード検索
if (
criteria.keyword &&
!product.name
.toLowerCase()
.includes(criteria.keyword.toLowerCase())
) {
return false;
}
// カテゴリ検索
if (
criteria.category &&
product.category !== criteria.category
) {
return false;
}
// ブランド検索
if (
criteria.brand &&
product.brand !== criteria.brand
) {
return false;
}
// 価格範囲
if (
criteria.minPrice &&
product.price < criteria.minPrice
) {
return false;
}
if (
criteria.maxPrice &&
product.price > criteria.maxPrice
) {
return false;
}
// 在庫状況
if (criteria.inStockOnly && !product.inStock) {
return false;
}
// 評価
if (
criteria.minRating &&
product.rating < criteria.minRating
) {
return false;
}
return true;
});
}
}
// 使用例
const productSearch = new ProductSearch();
// キーワード検索
console.log(
'iPhone検索:',
productSearch.searchByKeyword('iPhone')
);
// 価格範囲検索
console.log(
'10万円以下の商品:',
productSearch.searchByPriceRange(0, 100000)
);
// 複合検索
const searchCriteria = {
keyword: 'Apple',
minPrice: 50000,
maxPrice: 200000,
inStockOnly: true,
minRating: 4.5,
};
console.log(
'複合検索結果:',
productSearch.advancedSearch(searchCriteria)
);
データ分析での活用
データ分析では、大量のデータから特定の条件に一致するデータを抽出し、統計情報を取得することが重要です。
javascript// データ分析での活用例
class DataAnalyzer {
constructor() {
this.salesData = [
{
id: 1,
product: '商品A',
amount: 50000,
date: '2024-01-01',
region: '東京',
category: '電子機器',
},
{
id: 2,
product: '商品B',
amount: 30000,
date: '2024-01-02',
region: '大阪',
category: '食品',
},
{
id: 3,
product: '商品C',
amount: 80000,
date: '2024-01-03',
region: '東京',
category: '電子機器',
},
{
id: 4,
product: '商品D',
amount: 20000,
date: '2024-01-04',
region: '名古屋',
category: '衣類',
},
{
id: 5,
product: '商品E',
amount: 60000,
date: '2024-01-05',
region: '東京',
category: '食品',
},
{
id: 6,
product: '商品F',
amount: 40000,
date: '2024-01-06',
region: '大阪',
category: '電子機器',
},
{
id: 7,
product: '商品G',
amount: 70000,
date: '2024-01-07',
region: '福岡',
category: '衣類',
},
{
id: 8,
product: '商品H',
amount: 90000,
date: '2024-01-08',
region: '東京',
category: '電子機器',
},
];
}
// 地域別売上データを取得
getSalesByRegion(region) {
return _.filter(
this.salesData,
(sale) => sale.region === region
);
}
// カテゴリ別売上データを取得
getSalesByCategory(category) {
return _.filter(
this.salesData,
(sale) => sale.category === category
);
}
// 高額売上(5万円以上)を取得
getHighValueSales() {
return _.filter(
this.salesData,
(sale) => sale.amount >= 50000
);
}
// 特定期間の売上データを取得
getSalesByDateRange(startDate, endDate) {
return _.filter(this.salesData, (sale) => {
const saleDate = new Date(sale.date);
const start = new Date(startDate);
const end = new Date(endDate);
return saleDate >= start && saleDate <= end;
});
}
// 地域とカテゴリの組み合わせで売上を取得
getSalesByRegionAndCategory(region, category) {
return _.filter(
this.salesData,
(sale) =>
sale.region === region && sale.category === category
);
}
// 統計情報を計算
calculateStatistics(data) {
if (data.length === 0) return null;
const total = _.sumBy(data, 'amount');
const average = total / data.length;
const max = _.maxBy(data, 'amount');
const min = _.minBy(data, 'amount');
return {
count: data.length,
total,
average: Math.round(average),
max: max.amount,
min: min.amount,
};
}
// 地域別統計
getRegionalStatistics() {
const regions = _.uniq(_.map(this.salesData, 'region'));
const stats = {};
regions.forEach((region) => {
const regionalData = this.getSalesByRegion(region);
stats[region] =
this.calculateStatistics(regionalData);
});
return stats;
}
}
// 使用例
const analyzer = new DataAnalyzer();
// 東京の売上データ
const tokyoSales = analyzer.getSalesByRegion('東京');
console.log('東京の売上件数:', tokyoSales.length);
// 電子機器の売上データ
const electronicsSales =
analyzer.getSalesByCategory('電子機器');
console.log('電子機器の売上件数:', electronicsSales.length);
// 高額売上
const highValueSales = analyzer.getHighValueSales();
console.log('高額売上件数:', highValueSales.length);
// 地域別統計
const regionalStats = analyzer.getRegionalStatistics();
console.log('地域別統計:', regionalStats);
パフォーマンスとベストプラクティス
大量データでの処理最適化
大量のデータを扱う際は、パフォーマンスを意識した実装が重要です。filter 関数は配列の全要素をチェックするため、データ量が多いと処理時間が増加します。
javascript// 大量データでのパフォーマンス比較
const largeDataset = Array.from(
{ length: 100000 },
(_, i) => ({
id: i,
name: `Item ${i}`,
category:
i % 5 === 0
? 'A'
: i % 5 === 1
? 'B'
: i % 5 === 2
? 'C'
: i % 5 === 3
? 'D'
: 'E',
value: Math.floor(Math.random() * 1000),
isActive: i % 3 === 0,
})
);
// パフォーマンス測定関数
function measurePerformance(fn, name) {
const start = performance.now();
const result = fn();
const end = performance.now();
console.log(
`${name}: ${(end - start).toFixed(2)}ms, 結果件数: ${
result.length
}`
);
return result;
}
// 効率的な検索(条件を組み合わせて早期リターン)
const efficientSearch = () => {
return _.filter(largeDataset, (item) => {
// 最初に軽い条件をチェック
if (!item.isActive) return false;
if (item.category !== 'A') return false;
// 最後に重い条件をチェック
return item.value > 500;
});
};
// 非効率な検索(重い条件を先にチェック)
const inefficientSearch = () => {
return _.filter(largeDataset, (item) => {
// 重い条件を先にチェック
if (item.value > 500) {
if (item.isActive && item.category === 'A') {
return true;
}
}
return false;
});
};
// パフォーマンス測定
measurePerformance(efficientSearch, '効率的な検索');
measurePerformance(inefficientSearch, '非効率な検索');
メモリ使用量の考慮
大量のデータを扱う際は、メモリ使用量も考慮する必要があります。必要に応じて、データを分割して処理することを検討しましょう。
javascript// メモリ効率的な処理
class MemoryEfficientProcessor {
constructor(data, batchSize = 1000) {
this.data = data;
this.batchSize = batchSize;
}
// バッチ処理でフィルタリング
filterInBatches(predicate) {
const results = [];
for (
let i = 0;
i < this.data.length;
i += this.batchSize
) {
const batch = this.data.slice(i, i + this.batchSize);
const filteredBatch = _.filter(batch, predicate);
results.push(...filteredBatch);
// メモリ解放のためのガベージコレクションを促す
if (i % (this.batchSize * 10) === 0) {
// 実際のプロジェクトでは、ここでメモリ使用量を監視
console.log(
`処理済み: ${i + batch.length}/${
this.data.length
}`
);
}
}
return results;
}
// ストリーミング処理(最初のN件のみ取得)
filterFirstN(predicate, limit) {
const results = [];
for (
let i = 0;
i < this.data.length && results.length < limit;
i++
) {
if (predicate(this.data[i])) {
results.push(this.data[i]);
}
}
return results;
}
}
// 使用例
const processor = new MemoryEfficientProcessor(
largeDataset
);
// バッチ処理でフィルタリング
const batchResults = processor.filterInBatches(
(item) => item.isActive && item.value > 500
);
console.log('バッチ処理結果件数:', batchResults.length);
// 最初の100件のみ取得
const first100 = processor.filterFirstN(
(item) => item.isActive && item.value > 500,
100
);
console.log('最初の100件:', first100.length);
代替手段との比較
Lodash の filter・find 関数は強力ですが、状況によっては他の方法の方が適している場合があります。
javascript// 代替手段との比較
const users = [
{ id: 1, name: '田中太郎', age: 25, isActive: true },
{ id: 2, name: '佐藤花子', age: 30, isActive: false },
{ id: 3, name: '鈴木一郎', age: 22, isActive: true },
{ id: 4, name: '高橋美咲', age: 28, isActive: true },
];
// 1. Lodash filter(推奨:読みやすく、柔軟)
const lodashFilter = _.filter(
users,
(user) => user.isActive && user.age >= 25
);
// 2. ネイティブ Array.filter(シンプルな場合)
const nativeFilter = users.filter(
(user) => user.isActive && user.age >= 25
);
// 3. for文(パフォーマンスが重要な場合)
const forLoop = [];
for (let i = 0; i < users.length; i++) {
const user = users[i];
if (user.isActive && user.age >= 25) {
forLoop.push(user);
}
}
// 4. Map/Setを使った高速検索(大量データで頻繁に検索する場合)
const userMap = new Map();
users.forEach((user) => {
const key = `${user.isActive}-${user.age >= 25}`;
if (!userMap.has(key)) {
userMap.set(key, []);
}
userMap.get(key).push(user);
});
// 事前に分類されたデータから高速取得
const fastSearch = userMap.get('true-true') || [];
console.log('Lodash filter:', lodashFilter.length);
console.log('Native filter:', nativeFilter.length);
console.log('For loop:', forLoop.length);
console.log('Fast search:', fastSearch.length);
まとめ
Lodash のfilter
とfind
関数は、JavaScript でのデータ抽出において非常に強力なツールです。これらの関数を適切に使いこなすことで、読みやすく保守性の高いコードを書くことができます。
今回紹介した実践的な活用パターンを通じて、以下のポイントを押さえることができました:
- 基本構文の理解: filter は条件に一致するすべての要素を、find は最初の 1 つを取得
- 複雑な条件での絞り込み: 論理演算子を活用した柔軟な条件設定
- ネストしたオブジェクトの検索: 深い階層のデータでも効率的にアクセス
- 実践的な活用: ユーザー管理、商品検索、データ分析での具体的な実装例
- パフォーマンス最適化: 大量データでの処理効率とメモリ使用量の考慮
これらの知識を活用することで、より効率的で保守性の高いアプリケーションを開発できるようになります。特に、複雑なビジネスロジックを実装する際に、Lodash の filter・find 関数が大きな助けとなるでしょう。
実際のプロジェクトでは、データの特性やパフォーマンス要件に応じて、適切な方法を選択することが重要です。Lodash の柔軟性と読みやすさを活かしながら、必要に応じて他の手法も組み合わせることで、最適なソリューションを構築できます。
関連リンク
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来