Skip to content

mapValues

將對象的值映射為新的值,支持自定義轉換函數。

基礎用法

typescript
import { mapValues } from 'radash'

const obj = { name: 'Alice', age: 25, city: 'Beijing' }
const mapped = mapValues(obj, value => value.toString().toUpperCase())
console.log(mapped) // { name: 'ALICE', age: '25', city: 'BEIJING' }

語法

typescript
function mapValues<T extends Record<string, any>, U extends Record<string, any>>(
  obj: T,
  fn: (value: T[keyof T], key: keyof T) => any
): U

參數

  • obj (T): 要映射值的對象
  • fn (function): 映射函數,接收值和鍵名,返回新的值

返回值

返回映射後的新對象,鍵名保持不變。

示例

基本值映射

typescript
import { mapValues } from 'radash'

const obj = { name: 'Alice', age: 25, city: 'Beijing' }

// 轉換為大寫
const mapped1 = mapValues(obj, value => value.toString().toUpperCase())
console.log(mapped1) // { name: 'ALICE', age: '25', city: 'BEIJING' }

// 轉換為小寫
const mapped2 = mapValues(obj, value => value.toString().toLowerCase())
console.log(mapped2) // { name: 'alice', age: '25', city: 'beijing' }

// 添加前綴
const mapped3 = mapValues(obj, value => `value_${value}`)
console.log(mapped3) // { name: 'value_Alice', age: 'value_25', city: 'value_Beijing' }

數值轉換

typescript
import { mapValues } from 'radash'

const scores = { math: 85, english: 92, science: 78, history: 88 }

// 轉換為百分比
const percentages = mapValues(scores, value => `${value}%`)
console.log(percentages) // { math: '85%', english: '92%', science: '78%', history: '88%' }

// 轉換為等級
const grades = mapValues(scores, value => {
  if (value >= 90) return 'A'
  if (value >= 80) return 'B'
  if (value >= 70) return 'C'
  return 'D'
})
console.log(grades) // { math: 'B', english: 'A', science: 'C', history: 'B' }

// 轉換為布爾值
const passed = mapValues(scores, value => value >= 80)
console.log(passed) // { math: true, english: true, science: false, history: true }

字符串處理

typescript
import { mapValues } from 'radash'

const userInfo = {
  firstName: 'alice',
  lastName: 'smith',
  email: 'alice.smith@example.com',
  username: 'alice_smith'
}

// 首字母大寫
const capitalized = mapValues(userInfo, value => 
  value.charAt(0).toUpperCase() + value.slice(1)
)
console.log(capitalized)
// { firstName: 'Alice', lastName: 'Smith', email: 'Alice.smith@example.com', username: 'Alice_smith' }

// 移除特殊字符
const cleaned = mapValues(userInfo, value => 
  value.replace(/[^a-zA-Z0-9]/g, '')
)
console.log(cleaned)
// { firstName: 'alice', lastName: 'smith', email: 'alicesmithexamplecom', username: 'alice_smith' }

// 轉換為標題格式
const titled = mapValues(userInfo, value => 
  value.split(/[._-]/).map(word => 
    word.charAt(0).toUpperCase() + word.slice(1)
  ).join(' ')
)
console.log(titled)
// { firstName: 'Alice', lastName: 'Smith', email: 'Alice Smith Example Com', username: 'Alice Smith' }

數組值處理

typescript
import { mapValues } from 'radash'

const data = {
  numbers: [1, 2, 3, 4, 5],
  names: ['Alice', 'Bob', 'Charlie'],
  scores: [85, 92, 78, 88, 95]
}

// 計算數組長度
const lengths = mapValues(data, value => value.length)
console.log(lengths) // { numbers: 5, names: 3, scores: 5 }

// 計算數組平均值
const averages = mapValues(data, value => {
  if (Array.isArray(value) && value.every(v => typeof v === 'number')) {
    return value.reduce((sum, v) => sum + v, 0) / value.length
  }
  return value
})
console.log(averages) // { numbers: 3, names: ['Alice', 'Bob', 'Charlie'], scores: 87.6 }

// 過濾數組
const filtered = mapValues(data, value => {
  if (Array.isArray(value)) {
    return value.filter(v => typeof v === 'number' && v > 80)
  }
  return value
})
console.log(filtered) // { numbers: [1, 2, 3, 4, 5], names: ['Alice', 'Bob', 'Charlie'], scores: [85, 92, 88, 95] }

對象值處理

typescript
import { mapValues } from 'radash'

const users = {
  user1: { name: 'Alice', age: 25, active: true },
  user2: { name: 'Bob', age: 30, active: false },
  user3: { name: 'Charlie', age: 35, active: true }
}

// 提取特定屬性
const names = mapValues(users, value => value.name)
console.log(names) // { user1: 'Alice', user2: 'Bob', user3: 'Charlie' }

// 計算年齡總和
const totalAge = mapValues(users, value => value.age)
console.log(totalAge) // { user1: 25, user2: 30, user3: 35 }

// 檢查活躍狀態
const activeUsers = mapValues(users, value => value.active)
console.log(activeUsers) // { user1: true, user2: false, user3: true }

// 創建用戶摘要
const summaries = mapValues(users, value => ({
  name: value.name,
  status: value.active ? 'Active' : 'Inactive'
}))
console.log(summaries)
// {
//   user1: { name: 'Alice', status: 'Active' },
//   user2: { name: 'Bob', status: 'Inactive' },
//   user3: { name: 'Charlie', status: 'Active' }
// }

日期處理

typescript
import { mapValues } from 'radash'

const events = {
  meeting: new Date('2023-12-31T10:00:00'),
  deadline: new Date('2024-01-15T17:00:00'),
  birthday: new Date('1998-05-15T00:00:00')
}

// 轉換為日期字符串
const dateStrings = mapValues(events, value => value.toDateString())
console.log(dateStrings)
// { meeting: 'Sun Dec 31 2023', deadline: 'Mon Jan 15 2024', birthday: 'Fri May 15 1998' }

// 轉換為時間戳
const timestamps = mapValues(events, value => value.getTime())
console.log(timestamps)
// { meeting: 1704038400000, deadline: 1705320000000, birthday: 895104000000 }

// 計算距離現在的天數
const daysFromNow = mapValues(events, value => {
  const now = new Date()
  const diffTime = value.getTime() - now.getTime()
  return Math.ceil(diffTime / (1000 * 60 * 60 * 24))
})
console.log(daysFromNow) // { meeting: -1, deadline: 15, birthday: -9325 }

函數值處理

typescript
import { mapValues } from 'radash'

const handlers = {
  click: () => console.log('clicked'),
  submit: (data: any) => console.log('submitted', data),
  validate: (value: any) => value.length > 0,
  transform: (value: any) => value.toUpperCase()
}

// 獲取函數名稱
const functionNames = mapValues(handlers, value => value.name)
console.log(functionNames)
// { click: 'click', submit: 'submit', validate: 'validate', transform: 'transform' }

// 檢查是否為異步函數
const isAsync = mapValues(handlers, value => value.constructor.name === 'AsyncFunction')
console.log(isAsync) // { click: false, submit: false, validate: false, transform: false }

// 獲取函數參數數量
const paramCounts = mapValues(handlers, value => value.length)
console.log(paramCounts) // { click: 0, submit: 1, validate: 1, transform: 1 }

布爾值處理

typescript
import { mapValues } from 'radash'

const flags = {
  isActive: true,
  isVerified: false,
  isPremium: true,
  isBlocked: false,
  isPublic: true
}

// 轉換為字符串
const stringFlags = mapValues(flags, value => value ? 'Yes' : 'No')
console.log(stringFlags)
// { isActive: 'Yes', isVerified: 'No', isPremium: 'Yes', isBlocked: 'No', isPublic: 'Yes' }

// 轉換為數字
const numericFlags = mapValues(flags, value => value ? 1 : 0)
console.log(numericFlags)
// { isActive: 1, isVerified: 0, isPremium: 1, isBlocked: 0, isPublic: 1 }

// 轉換為圖標
const iconFlags = mapValues(flags, value => value ? '✅' : '❌')
console.log(iconFlags)
// { isActive: '✅', isVerified: '❌', isPremium: '✅', isBlocked: '❌', isPublic: '✅' }

API響應處理

typescript
import { mapValues } from 'radash'

const apiResponse = {
  status: 200,
  data: { id: 1, name: 'Alice' },
  message: 'Success',
  timestamp: new Date().toISOString()
}

// 轉換為用戶友好的消息
const userFriendly = mapValues(apiResponse, (value, key) => {
  switch (key) {
    case 'status':
      return value === 200 ? 'OK' : 'Error'
    case 'message':
      return value.toUpperCase()
    case 'timestamp':
      return new Date(value).toLocaleString()
    default:
      return value
  }
})
console.log(userFriendly)
// { status: 'OK', data: { id: 1, name: 'Alice' }, message: 'SUCCESS', timestamp: '12/31/2023, 12:00:00 PM' }

// 提取關鍵信息
const summary = mapValues(apiResponse, (value, key) => {
  if (key === 'data') return 'Data available'
  if (key === 'timestamp') return 'Recent'
  return value
})
console.log(summary)
// { status: 200, data: 'Data available', message: 'Success', timestamp: 'Recent' }

配置對象處理

typescript
import { mapValues } from 'radash'

const config = {
  port: 3000,
  host: 'localhost',
  debug: true,
  timeout: 5000,
  retries: 3
}

// 轉換為環境變量格式
const envVars = mapValues(config, (value, key) => {
  const envKey = key.toUpperCase()
  return `${envKey}=${value}`
})
console.log(envVars)
// { port: 'PORT=3000', host: 'HOST=localhost', debug: 'DEBUG=true', timeout: 'TIMEOUT=5000', retries: 'RETRIES=3' }

// 轉換為命令行參數
const cliArgs = mapValues(config, (value, key) => {
  return `--${key}=${value}`
})
console.log(cliArgs)
// { port: '--port=3000', host: '--host=localhost', debug: '--debug=true', timeout: '--timeout=5000', retries: '--retries=3' }

// 驗證配置值
const validation = mapValues(config, (value, key) => {
  if (key === 'port' && (value < 1 || value > 65535)) return 'Invalid port'
  if (key === 'timeout' && value < 0) return 'Invalid timeout'
  if (key === 'retries' && value < 0) return 'Invalid retries'
  return 'Valid'
})
console.log(validation)
// { port: 'Valid', host: 'Valid', debug: 'Valid', timeout: 'Valid', retries: 'Valid' }

錯誤對象處理

typescript
import { mapValues } from 'radash'

const errors = {
  validation: new Error('Invalid input'),
  network: new Error('Connection failed'),
  auth: new Error('Unauthorized'),
  server: new Error('Internal server error')
}

// 提取錯誤消息
const messages = mapValues(errors, value => value.message)
console.log(messages)
// { validation: 'Invalid input', network: 'Connection failed', auth: 'Unauthorized', server: 'Internal server error' }

// 轉換為錯誤代碼
const errorCodes = mapValues(errors, (value, key) => {
  const codeMap: Record<string, string> = {
    validation: 'VAL_001',
    network: 'NET_001',
    auth: 'AUTH_001',
    server: 'SRV_001'
  }
  return codeMap[key] || 'UNKNOWN'
})
console.log(errorCodes)
// { validation: 'VAL_001', network: 'NET_001', auth: 'AUTH_001', server: 'SRV_001' }

// 轉換為用戶友好的錯誤
const userErrors = mapValues(errors, value => {
  const message = value.message
  if (message.includes('Invalid')) return 'Please check your input'
  if (message.includes('Connection')) return 'Please check your internet connection'
  if (message.includes('Unauthorized')) return 'Please log in again'
  return 'Something went wrong'
})
console.log(userErrors)
// { validation: 'Please check your input', network: 'Please check your internet connection', auth: 'Please log in again', server: 'Something went wrong' }

數據庫記錄處理

typescript
import { mapValues } from 'radash'

const dbRecord = {
  id: 1,
  name: 'Product A',
  price: 29.99,
  category: 'Electronics',
  inStock: true,
  createdAt: new Date('2023-01-01'),
  updatedAt: new Date('2023-12-31')
}

// 轉換為顯示格式
const displayFormat = mapValues(dbRecord, (value, key) => {
  if (key === 'price') return `$${value.toFixed(2)}`
  if (key === 'inStock') return value ? 'In Stock' : 'Out of Stock'
  if (key === 'createdAt' || key === 'updatedAt') return value.toLocaleDateString()
  return value
})
console.log(displayFormat)
// { id: 1, name: 'Product A', price: '$29.99', category: 'Electronics', inStock: 'In Stock', createdAt: '1/1/2023', updatedAt: '12/31/2023' }

// 轉換為API響應格式
const apiFormat = mapValues(dbRecord, (value, key) => {
  if (key === 'createdAt' || key === 'updatedAt') return value.toISOString()
  if (key === 'inStock') return value ? 1 : 0
  return value
})
console.log(apiFormat)
// { id: 1, name: 'Product A', price: 29.99, category: 'Electronics', inStock: 1, createdAt: '2023-01-01T00:00:00.000Z', updatedAt: '2023-12-31T00:00:00.000Z' }

嵌套對象處理

typescript
import { mapValues } from 'radash'

const nestedData = {
  user: {
    profile: { name: 'Alice', age: 25 },
    settings: { theme: 'dark', notifications: true }
  },
  product: {
    details: { name: 'Product A', price: 29.99 },
    inventory: { stock: 100, reserved: 5 }
  }
}

// 扁平化嵌套對象
const flattened = mapValues(nestedData, value => {
  if (typeof value === 'object' && value !== null) {
    return Object.entries(value).reduce((acc, [key, val]) => {
      if (typeof val === 'object' && val !== null) {
        Object.entries(val).forEach(([subKey, subVal]) => {
          acc[`${key}_${subKey}`] = subVal
        })
      } else {
        acc[key] = val
      }
      return acc
    }, {} as Record<string, any>)
  }
  return value
})
console.log(flattened)
// {
//   user: { profile_name: 'Alice', profile_age: 25, settings_theme: 'dark', settings_notifications: true },
//   product: { details_name: 'Product A', details_price: 29.99, inventory_stock: 100, inventory_reserved: 5 }
// }

條件映射

typescript
import { mapValues } from 'radash'

const data = {
  name: 'Alice',
  age: 25,
  email: 'alice@example.com',
  phone: null,
  address: undefined
}

// 條件映射
const processed = mapValues(data, (value, key) => {
  if (value === null || value === undefined) {
    return 'Not provided'
  }
  
  if (key === 'email') {
    return value.replace('@', ' [at] ')
  }
  
  if (key === 'age') {
    return `${value} years old`
  }
  
  return value
})
console.log(processed)
// { name: 'Alice', age: '25 years old', email: 'alice [at] example.com', phone: 'Not provided', address: 'Not provided' }

注意事項

  1. 映射函數: 映射函數接收值和鍵名作為參數
  2. 不可變性: 返回新對象,不修改原對象
  3. 類型安全: 支持TypeScript類型推斷
  4. 嵌套對象: 只處理頂層值,不遞歸處理嵌套對象
  5. 性能: 適合處理中小型對象

與其他方法的區別

  • Object.keys(): 獲取對象的所有鍵
  • mapValues(): 映射值並返回新對象
  • mapKeys(): 映射鍵名並返回新對象
  • mapEntries(): 映射鍵值對並返回新對象

實際應用場景

  1. 數據轉換: 轉換API響應數據格式
  2. 顯示格式化: 格式化數據用於顯示
  3. 數據驗證: 驗證和清理數據
  4. 配置處理: 處理配置對象
  5. 錯誤處理: 統一處理錯誤信息

Released under the MIT License.