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.