Skip to content

crush

將嵌套對象扁平化為單層對象,使用點號分隔的鍵名。

基礎用法

typescript
import { crush } from 'radash'

const nested = {
  user: {
    profile: {
      name: 'Alice',
      age: 25
    },
    settings: {
      theme: 'dark'
    }
  }
}

const flattened = crush(nested)
console.log(flattened)
// {
//   'user.profile.name': 'Alice',
//   'user.profile.age': 25,
//   'user.settings.theme': 'dark'
// }

語法

typescript
function crush<T extends Record<string, any>>(obj: T): Record<string, any>

參數

  • obj (T): 要扁平化的嵌套對象

返回值

返回扁平化的對象,所有嵌套屬性都使用點號分隔的鍵名。

示例

基本扁平化

typescript
import { crush } from 'radash'

const obj = {
  name: 'Alice',
  address: {
    city: 'Beijing',
    country: 'China'
  }
}

const result = crush(obj)
console.log(result)
// {
//   name: 'Alice',
//   'address.city': 'Beijing',
//   'address.country': 'China'
// }

處理深層嵌套

typescript
import { crush } from 'radash'

const deepNested = {
  user: {
    profile: {
      personal: {
        name: 'Alice',
        age: 25,
        contact: {
          email: 'alice@example.com',
          phone: '123-456-7890'
        }
      },
      preferences: {
        theme: 'dark',
        language: 'en'
      }
    },
    settings: {
      notifications: {
        email: true,
        push: false
      }
    }
  }
}

const result = crush(deepNested)
console.log(result)
// {
//   'user.profile.personal.name': 'Alice',
//   'user.profile.personal.age': 25,
//   'user.profile.personal.contact.email': 'alice@example.com',
//   'user.profile.personal.contact.phone': '123-456-7890',
//   'user.profile.preferences.theme': 'dark',
//   'user.profile.preferences.language': 'en',
//   'user.settings.notifications.email': true,
//   'user.settings.notifications.push': false
// }

處理數組

typescript
import { crush } from 'radash'

const objWithArrays = {
  user: {
    name: 'Alice',
    hobbies: ['reading', 'swimming'],
    skills: [
      { name: 'JavaScript', level: 'expert' },
      { name: 'TypeScript', level: 'intermediate' }
    ]
  }
}

const result = crush(objWithArrays)
console.log(result)
// {
//   'user.name': 'Alice',
//   'user.hobbies.0': 'reading',
//   'user.hobbies.1': 'swimming',
//   'user.skills.0.name': 'JavaScript',
//   'user.skills.0.level': 'expert',
//   'user.skills.1.name': 'TypeScript',
//   'user.skills.1.level': 'intermediate'
// }

處理混合數據類型

typescript
import { crush } from 'radash'

const mixedData = {
  user: {
    name: 'Alice',
    age: 25,
    isActive: true,
    score: 95.5,
    tags: ['admin', 'premium'],
    metadata: {
      createdAt: new Date('2023-01-01'),
      lastLogin: new Date('2023-12-31')
    }
  }
}

const result = crush(mixedData)
console.log(result)
// {
//   'user.name': 'Alice',
//   'user.age': 25,
//   'user.isActive': true,
//   'user.score': 95.5,
//   'user.tags.0': 'admin',
//   'user.tags.1': 'premium',
//   'user.metadata.createdAt': 2023-01-01T00:00:00.000Z,
//   'user.metadata.lastLogin': 2023-12-31T00:00:00.000Z
// }

處理空值和undefined

typescript
import { crush } from 'radash'

const objWithNulls = {
  user: {
    name: 'Alice',
    email: null,
    phone: undefined,
    address: {
      city: 'Beijing',
      zipCode: null
    }
  }
}

const result = crush(objWithNulls)
console.log(result)
// {
//   'user.name': 'Alice',
//   'user.email': null,
//   'user.phone': undefined,
//   'user.address.city': 'Beijing',
//   'user.address.zipCode': null
// }

處理函數和對象

typescript
import { crush } from 'radash'

const objWithFunctions = {
  user: {
    name: 'Alice',
    greet: () => 'Hello!',
    config: {
      handler: function() { return 'config' },
      data: { id: 1 }
    }
  }
}

const result = crush(objWithFunctions)
console.log(result)
// {
//   'user.name': 'Alice',
//   'user.greet': [Function: greet],
//   'user.config.handler': [Function: handler],
//   'user.config.data.id': 1
// }

處理復雜配置對象

typescript
import { crush } from 'radash'

const config = {
  server: {
    host: 'localhost',
    port: 3000,
    cors: {
      origin: ['http://localhost:3000', 'https://example.com'],
      credentials: true
    }
  },
  database: {
    url: 'postgresql://localhost:5432/mydb',
    pool: {
      min: 1,
      max: 10
    }
  },
  api: {
    version: 'v1',
    rateLimit: {
      windowMs: 15 * 60 * 1000,
      max: 100
    }
  }
}

const result = crush(config)
console.log(result)
// {
//   'server.host': 'localhost',
//   'server.port': 3000,
//   'server.cors.origin.0': 'http://localhost:3000',
//   'server.cors.origin.1': 'https://example.com',
//   'server.cors.credentials': true,
//   'database.url': 'postgresql://localhost:5432/mydb',
//   'database.pool.min': 1,
//   'database.pool.max': 10,
//   'api.version': 'v1',
//   'api.rateLimit.windowMs': 900000,
//   'api.rateLimit.max': 100
// }

處理API響應數據

typescript
import { crush } from 'radash'

const apiResponse = {
  status: 200,
  data: {
    user: {
      id: 1,
      name: 'Alice',
      profile: {
        avatar: 'https://example.com/avatar.jpg',
        bio: 'Software Developer'
      },
      posts: [
        { id: 1, title: 'First Post', content: 'Hello World' },
        { id: 2, title: 'Second Post', content: 'Another post' }
      ]
    }
  },
  meta: {
    timestamp: new Date(),
    requestId: 'req_123456'
  }
}

const result = crush(apiResponse)
console.log(result)
// {
//   status: 200,
//   'data.user.id': 1,
//   'data.user.name': 'Alice',
//   'data.user.profile.avatar': 'https://example.com/avatar.jpg',
//   'data.user.profile.bio': 'Software Developer',
//   'data.user.posts.0.id': 1,
//   'data.user.posts.0.title': 'First Post',
//   'data.user.posts.0.content': 'Hello World',
//   'data.user.posts.1.id': 2,
//   'data.user.posts.1.title': 'Second Post',
//   'data.user.posts.1.content': 'Another post',
//   'meta.timestamp': 2023-12-31T12:00:00.000Z,
//   'meta.requestId': 'req_123456'
// }

處理表單數據

typescript
import { crush } from 'radash'

const formData = {
  personal: {
    firstName: 'Alice',
    lastName: 'Smith',
    email: 'alice@example.com'
  },
  address: {
    street: '123 Main St',
    city: 'Beijing',
    country: 'China',
    zipCode: '100000'
  },
  preferences: {
    newsletter: true,
    notifications: {
      email: true,
      sms: false,
      push: true
    }
  }
}

const result = crush(formData)
console.log(result)
// {
//   'personal.firstName': 'Alice',
//   'personal.lastName': 'Smith',
//   'personal.email': 'alice@example.com',
//   'address.street': '123 Main St',
//   'address.city': 'Beijing',
//   'address.country': 'China',
//   'address.zipCode': '100000',
//   'preferences.newsletter': true,
//   'preferences.notifications.email': true,
//   'preferences.notifications.sms': false,
//   'preferences.notifications.push': true
// }

處理嵌套對象數組

typescript
import { crush } from 'radash'

const complexData = {
  users: [
    {
      id: 1,
      name: 'Alice',
      roles: ['admin', 'user'],
      profile: {
        age: 25,
        skills: ['JavaScript', 'TypeScript']
      }
    },
    {
      id: 2,
      name: 'Bob',
      roles: ['user'],
      profile: {
        age: 30,
        skills: ['Python', 'Java']
      }
    }
  ],
  settings: {
    theme: 'dark',
    features: {
      advanced: true,
      beta: false
    }
  }
}

const result = crush(complexData)
console.log(result)
// {
//   'users.0.id': 1,
//   'users.0.name': 'Alice',
//   'users.0.roles.0': 'admin',
//   'users.0.roles.1': 'user',
//   'users.0.profile.age': 25,
//   'users.0.profile.skills.0': 'JavaScript',
//   'users.0.profile.skills.1': 'TypeScript',
//   'users.1.id': 2,
//   'users.1.name': 'Bob',
//   'users.1.roles.0': 'user',
//   'users.1.profile.age': 30,
//   'users.1.profile.skills.0': 'Python',
//   'users.1.profile.skills.1': 'Java',
//   'settings.theme': 'dark',
//   'settings.features.advanced': true,
//   'settings.features.beta': false
// }

處理數據庫查詢結果

typescript
import { crush } from 'radash'

const dbResult = {
  id: 1,
  name: 'Product A',
  category: {
    id: 5,
    name: 'Electronics',
    parent: {
      id: 1,
      name: 'Technology'
    }
  },
  variants: [
    {
      id: 101,
      color: 'Red',
      price: 99.99,
      stock: {
        quantity: 50,
        location: 'Warehouse A'
      }
    },
    {
      id: 102,
      color: 'Blue',
      price: 89.99,
      stock: {
        quantity: 30,
        location: 'Warehouse B'
      }
    }
  ]
}

const result = crush(dbResult)
console.log(result)
// {
//   id: 1,
//   name: 'Product A',
//   'category.id': 5,
//   'category.name': 'Electronics',
//   'category.parent.id': 1,
//   'category.parent.name': 'Technology',
//   'variants.0.id': 101,
//   'variants.0.color': 'Red',
//   'variants.0.price': 99.99,
//   'variants.0.stock.quantity': 50,
//   'variants.0.stock.location': 'Warehouse A',
//   'variants.1.id': 102,
//   'variants.1.color': 'Blue',
//   'variants.1.price': 89.99,
//   'variants.1.stock.quantity': 30,
//   'variants.1.stock.location': 'Warehouse B'
// }

處理錯誤對象

typescript
import { crush } from 'radash'

const errorObject = {
  name: 'ValidationError',
  message: 'Invalid input',
  details: {
    field: 'email',
    value: 'invalid-email',
    constraints: {
      format: 'Must be a valid email',
      required: 'Email is required'
    }
  },
  stack: 'Error: Invalid input\n    at validate...'
}

const result = crush(errorObject)
console.log(result)
// {
//   name: 'ValidationError',
//   message: 'Invalid input',
//   'details.field': 'email',
//   'details.value': 'invalid-email',
//   'details.constraints.format': 'Must be a valid email',
//   'details.constraints.required': 'Email is required',
//   stack: 'Error: Invalid input\n    at validate...'
// }

處理日志數據

typescript
import { crush } from 'radash'

const logEntry = {
  timestamp: new Date(),
  level: 'ERROR',
  message: 'Database connection failed',
  context: {
    userId: 123,
    requestId: 'req_456',
    metadata: {
      ip: '192.168.1.1',
      userAgent: 'Mozilla/5.0...'
    }
  },
  error: {
    code: 'ECONNREFUSED',
    message: 'Connection refused',
    stack: 'Error: Connection refused...'
  }
}

const result = crush(logEntry)
console.log(result)
// {
//   timestamp: 2023-12-31T12:00:00.000Z,
//   level: 'ERROR',
//   message: 'Database connection failed',
//   'context.userId': 123,
//   'context.requestId': 'req_456',
//   'context.metadata.ip': '192.168.1.1',
//   'context.metadata.userAgent': 'Mozilla/5.0...',
//   'error.code': 'ECONNREFUSED',
//   'error.message': 'Connection refused',
//   'error.stack': 'Error: Connection refused...'
// }

處理空對象和邊界情況

typescript
import { crush } from 'radash'

// 空對象
console.log(crush({})) // {}

// 只有頂層屬性
console.log(crush({ a: 1, b: 2 })) // { a: 1, b: 2 }

// 包含空對象
console.log(crush({ a: 1, b: {}, c: 3 })) // { a: 1, b: {}, c: 3 }

// 包含空數組
console.log(crush({ a: 1, b: [], c: 3 })) // { a: 1, b: [], c: 3 }

// 包含null和undefined
console.log(crush({ a: 1, b: null, c: undefined })) // { a: 1, b: null, c: undefined }

注意事項

  1. 數組索引: 數組元素使用數字索引,如 array.0, array.1
  2. 嵌套層級: 支持任意深度的嵌套對象
  3. 數據類型: 保持原始數據類型,包括null、undefined、函數等
  4. 循環引用: 不處理循環引用,可能導致無限遞歸
  5. 性能: 對於大型嵌套對象,扁平化操作可能較慢

與其他方法的區別

  • Object.assign(): 淺合並對象
  • crush(): 將嵌套對象完全扁平化
  • flatten(): 通常用於數組扁平化
  • pick(): 選擇特定屬性
  • omit(): 排除特定屬性

實際應用場景

  1. 數據庫查詢: 將嵌套查詢結果扁平化
  2. API響應: 處理復雜的API響應數據
  3. 表單處理: 扁平化表單數據結構
  4. 配置管理: 處理嵌套的配置對象
  5. 日志分析: 扁平化日志數據結構

Released under the MIT License.