Skip to content

mapEntries

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

基礎用法

typescript
import { mapEntries } from 'radash'

const obj = { name: 'Alice', age: 25, city: 'Beijing' }
const mapped = mapEntries(obj, (key, value) => [key.toUpperCase(), value])
console.log(mapped) // { NAME: 'Alice', AGE: 25, CITY: 'Beijing' }

語法

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

參數

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

返回值

返回映射後的新對象。

示例

基本鍵值映射

typescript
import { mapEntries } from 'radash'

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

// 將鍵轉換為大寫
const mapped1 = mapEntries(obj, (key, value) => [key.toUpperCase(), value])
console.log(mapped1) // { NAME: 'Alice', AGE: 25, CITY: 'Beijing' }

// 將鍵轉換為小寫
const mapped2 = mapEntries(obj, (key, value) => [key.toLowerCase(), value])
console.log(mapped2) // { name: 'Alice', age: 25, city: 'Beijing' }

自定義鍵值轉換

typescript
import { mapEntries } from 'radash'

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

// 添加前綴到鍵名
const mapped1 = mapEntries(obj, (key, value) => [`user_${key}`, value])
console.log(mapped1) // { user_name: 'Alice', user_age: 25, user_city: 'Beijing' }

// 轉換值為字符串
const mapped2 = mapEntries(obj, (key, value) => [key, String(value)])
console.log(mapped2) // { name: 'Alice', age: '25', city: 'Beijing' }

// 同時轉換鍵和值
const mapped3 = mapEntries(obj, (key, value) => [
  key.toUpperCase(),
  typeof value === 'string' ? value.toUpperCase() : value
])
console.log(mapped3) // { NAME: 'ALICE', AGE: 25, CITY: 'BEIJING' }

處理不同類型的值

typescript
import { mapEntries } from 'radash'

const obj = {
  name: 'Alice',
  age: 25,
  isActive: true,
  score: 95.5,
  tags: ['admin', 'user']
}

// 根據值類型進行不同處理
const mapped = mapEntries(obj, (key, value) => {
  if (typeof value === 'string') {
    return [`${key}_str`, value.toUpperCase()]
  } else if (typeof value === 'number') {
    return [`${key}_num`, value * 2]
  } else if (typeof value === 'boolean') {
    return [`${key}_bool`, value ? 'YES' : 'NO']
  } else if (Array.isArray(value)) {
    return [`${key}_arr`, value.join(', ')]
  }
  return [key, value]
})

console.log(mapped)
// {
//   name_str: 'ALICE',
//   age_num: 50,
//   isActive_bool: 'YES',
//   score_num: 191,
//   tags_arr: 'admin, user'
// }

處理空值和undefined

typescript
import { mapEntries } from 'radash'

const obj = {
  name: 'Alice',
  email: null,
  phone: undefined,
  address: ''
}

// 過濾空值並轉換鍵名
const mapped = mapEntries(obj, (key, value) => {
  if (value == null) {
    return [`${key}_null`, 'N/A']
  }
  return [key, value]
})

console.log(mapped)
// {
//   name: 'Alice',
//   email_null: 'N/A',
//   phone_null: 'N/A',
//   address: ''
// }

處理嵌套對象

typescript
import { mapEntries } from 'radash'

const obj = {
  user: { name: 'Alice', age: 25 },
  settings: { theme: 'dark', language: 'en' }
}

// 扁平化嵌套對象
const mapped = mapEntries(obj, (key, value) => {
  if (typeof value === 'object' && value !== null) {
    return Object.entries(value).map(([subKey, subValue]) => [
      `${key}_${subKey}`,
      subValue
    ])
  }
  return [key, value]
})

console.log(mapped)
// {
//   user_name: 'Alice',
//   user_age: 25,
//   settings_theme: 'dark',
//   settings_language: 'en'
// }

處理函數值

typescript
import { mapEntries } from 'radash'

const obj = {
  name: 'Alice',
  handler: () => 'hello',
  validator: function() { return true },
  processor: (x: number) => x * 2
}

// 轉換函數為字符串描述
const mapped = mapEntries(obj, (key, value) => {
  if (typeof value === 'function') {
    return [`${key}_fn`, value.toString()]
  }
  return [key, value]
})

console.log(mapped)
// {
//   name: 'Alice',
//   handler_fn: '() => \'hello\'',
//   validator_fn: 'function() { return true }',
//   processor_fn: '(x) => x * 2'
// }

處理日期值

typescript
import { mapEntries } from 'radash'

const obj = {
  createdAt: new Date('2023-01-01'),
  updatedAt: new Date('2023-12-31'),
  birthday: new Date('1998-05-15')
}

// 轉換日期為ISO字符串
const mapped = mapEntries(obj, (key, value) => {
  if (value instanceof Date) {
    return [`${key}_iso`, value.toISOString()]
  }
  return [key, value]
})

console.log(mapped)
// {
//   createdAt_iso: '2023-01-01T00:00:00.000Z',
//   updatedAt_iso: '2023-12-31T00:00:00.000Z',
//   birthday_iso: '1998-05-15T00:00:00.000Z'
// }

處理布爾值

typescript
import { mapEntries } from 'radash'

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

// 轉換布爾值為字符串
const mapped = mapEntries(obj, (key, value) => {
  if (typeof value === 'boolean') {
    return [`${key}_str`, value ? 'YES' : 'NO']
  }
  return [key, value]
})

console.log(mapped)
// {
//   isActive_str: 'YES',
//   isVerified_str: 'NO',
//   isPremium_str: 'YES',
//   isBlocked_str: 'NO'
// }

處理數字值

typescript
import { mapEntries } from 'radash'

const obj = {
  id: 1,
  score: 95.5,
  rating: 4.8,
  count: 0,
  price: -10.99
}

// 轉換數字為不同格式
const mapped = mapEntries(obj, (key, value) => {
  if (typeof value === 'number') {
    return [
      `${key}_formatted`,
      value < 0 ? `(${Math.abs(value)})` : value.toString()
    ]
  }
  return [key, value]
})

console.log(mapped)
// {
//   id_formatted: '1',
//   score_formatted: '95.5',
//   rating_formatted: '4.8',
//   count_formatted: '0',
//   price_formatted: '(10.99)'
// }

處理API響應數據

typescript
import { mapEntries } from 'radash'

const apiResponse = {
  status: 200,
  data: { id: 1, name: 'Alice' },
  meta: { timestamp: new Date(), requestId: 'req_123456' }
}

// 添加前綴並轉換值
const mapped = mapEntries(apiResponse, (key, value) => {
  const newKey = `api_${key}`
  if (key === 'status') {
    return [newKey, value === 200 ? 'SUCCESS' : 'ERROR']
  }
  if (key === 'data') {
    return [newKey, JSON.stringify(value)]
  }
  if (key === 'meta') {
    return [newKey, value.timestamp.toISOString()]
  }
  return [newKey, value]
})

console.log(mapped)
// {
//   api_status: 'SUCCESS',
//   api_data: '{"id":1,"name":"Alice"}',
//   api_meta: '2023-12-31T12:00:00.000Z'
// }

處理表單數據

typescript
import { mapEntries } from 'radash'

const formData = {
  personal: {
    firstName: 'Alice',
    lastName: 'Smith',
    email: 'alice@example.com'
  },
  address: {
    street: '123 Main St',
    city: 'Beijing',
    country: 'China'
  }
}

// 扁平化表單數據
const mapped = mapEntries(formData, (key, value) => {
  if (typeof value === 'object' && value !== null) {
    return Object.entries(value).map(([subKey, subValue]) => [
      `${key}_${subKey}`,
      subValue
    ])
  }
  return [key, value]
})

console.log(mapped)
// {
//   personal_firstName: 'Alice',
//   personal_lastName: 'Smith',
//   personal_email: 'alice@example.com',
//   address_street: '123 Main St',
//   address_city: 'Beijing',
//   address_country: 'China'
// }

處理錯誤對象

typescript
import { mapEntries } from 'radash'

const errorObject = {
  name: 'ValidationError',
  message: 'Invalid input',
  details: {
    field: 'email',
    value: 'invalid-email',
    constraints: { format: 'Must be a valid email' }
  }
}

// 轉換錯誤對象為日志格式
const mapped = mapEntries(errorObject, (key, value) => {
  const newKey = `error_${key}`
  if (key === 'details') {
    return [newKey, JSON.stringify(value)]
  }
  return [newKey, value]
})

console.log(mapped)
// {
//   error_name: 'ValidationError',
//   error_message: 'Invalid input',
//   error_details: '{"field":"email","value":"invalid-email","constraints":{"format":"Must be a valid email"}}'
// }

處理日志數據

typescript
import { mapEntries } from 'radash'

const logEntry = {
  timestamp: new Date(),
  level: 'ERROR',
  message: 'Database connection failed',
  context: { userId: 123, requestId: 'req_456' },
  error: { code: 'ECONNREFUSED', message: 'Connection refused' }
}

// 轉換日志數據為結構化格式
const mapped = mapEntries(logEntry, (key, value) => {
  const newKey = `log_${key}`
  if (value instanceof Date) {
    return [newKey, value.toISOString()]
  }
  if (typeof value === 'object' && value !== null) {
    return [newKey, JSON.stringify(value)]
  }
  return [newKey, value]
})

console.log(mapped)
// {
//   log_timestamp: '2023-12-31T12:00:00.000Z',
//   log_level: 'ERROR',
//   log_message: 'Database connection failed',
//   log_context: '{"userId":123,"requestId":"req_456"}',
//   log_error: '{"code":"ECONNREFUSED","message":"Connection refused"}'
// }

處理數據庫查詢結果

typescript
import { mapEntries } from 'radash'

const dbResult = {
  id: 1,
  name: 'Product A',
  category: {
    id: 5,
    name: 'Electronics'
  },
  variants: [
    { id: 101, color: 'Red', price: 99.99 },
    { id: 102, color: 'Blue', price: 89.99 }
  ]
}

// 轉換數據庫結果為API格式
const mapped = mapEntries(dbResult, (key, value) => {
  const newKey = `db_${key}`
  if (key === 'category') {
    return [newKey, JSON.stringify(value)]
  }
  if (key === 'variants') {
    return [newKey, value.length.toString()]
  }
  return [newKey, value]
})

console.log(mapped)
// {
//   db_id: 1,
//   db_name: 'Product A',
//   db_category: '{"id":5,"name":"Electronics"}',
//   db_variants: '2'
// }

處理配置對象

typescript
import { mapEntries } from 'radash'

const config = {
  server: {
    host: 'localhost',
    port: 3000
  },
  database: {
    url: 'postgresql://localhost:5432/mydb',
    pool: { min: 1, max: 10 }
  },
  api: {
    version: 'v1',
    rateLimit: { windowMs: 900000, max: 100 }
  }
}

// 扁平化配置對象
const mapped = mapEntries(config, (key, value) => {
  if (typeof value === 'object' && value !== null) {
    return Object.entries(value).map(([subKey, subValue]) => [
      `${key}_${subKey}`,
      subValue
    ])
  }
  return [key, value]
})

console.log(mapped)
// {
//   server_host: 'localhost',
//   server_port: 3000,
//   database_url: 'postgresql://localhost:5432/mydb',
//   database_pool: { min: 1, max: 10 },
//   api_version: 'v1',
//   api_rateLimit: { windowMs: 900000, max: 100 }
// }

處理空對象和邊界情況

typescript
import { mapEntries } from 'radash'

// 空對象
console.log(mapEntries({}, (key, value) => [key, value])) // {}

// 只有一個鍵值對
console.log(mapEntries({ key: 'value' }, (key, value) => [key.toUpperCase(), value]))
// { KEY: 'value' }

// 包含空值
console.log(mapEntries({ a: 1, b: null, c: undefined }, (key, value) => [
  key,
  value ?? 'default'
]))
// { a: 1, b: 'default', c: 'default' }

處理復雜映射場景

typescript
import { mapEntries } from 'radash'

const complexObj = {
  user: {
    profile: {
      name: 'Alice',
      age: 25
    },
    settings: {
      theme: 'dark',
      language: 'en'
    }
  },
  system: {
    version: '1.0.0',
    environment: 'production'
  }
}

// 深度扁平化復雜對象
const mapped = mapEntries(complexObj, (key, value) => {
  const flatten = (obj: any, prefix = ''): [string, any][] => {
    return Object.entries(obj).flatMap(([k, v]) => {
      const newKey = prefix ? `${prefix}_${k}` : k
      if (typeof v === 'object' && v !== null && !Array.isArray(v)) {
        return flatten(v, newKey)
      }
      return [[newKey, v]]
    })
  }

  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
    return flatten(value, key)
  }
  return [key, value]
})

console.log(mapped)
// {
//   user_profile_name: 'Alice',
//   user_profile_age: 25,
//   user_settings_theme: 'dark',
//   user_settings_language: 'en',
//   system_version: '1.0.0',
//   system_environment: 'production'
// }

注意事項

  1. 映射函數: 映射函數必須返回 [string, any] 格式的數組
  2. 嵌套對象: 需要手動處理嵌套對象的映射
  3. 數組值: 數組值會保持原樣,需要特殊處理
  4. 不可變性: 返回新對象,不修改原對象
  5. 類型安全: 支持TypeScript類型推斷

與其他方法的區別

  • Object.entries(): 獲取鍵值對數組
  • mapEntries(): 映射鍵值對並返回新對象
  • mapKeys(): 只映射鍵名
  • mapValues(): 只映射值

實際應用場景

  1. 數據轉換: 在不同系統間轉換數據格式
  2. API適配: 適配不同API的數據格式
  3. 配置處理: 處理配置對象的結構轉換
  4. 日志格式化: 格式化日志數據的結構
  5. 數據庫映射: 映射數據庫字段名和值

Released under the MIT License.