☰
30 seconds of code:用 listify 在 JavaScript 对象与对象数组之间自由转换
2026/10/4 1:53:42 网站建设 项目流程
  • 教程
  • 文档

【免费下载链接】30-seconds-of-code

Coding articles to level up your development skills

项目地址:https://gitcode.com/gh_mirrors/30/30-seconds-of-code
点击查看免费下载

导读

本指南围绕 30 seconds of code 仓库中的经典片段 listify 展开,讲解如何借助Object.entries()与Object.fromEntries()这对互补的 API,配合自定义映射函数,在「字典型对象」与「对象数组」之间双向转换。读完本文,你将掌握listify与delistify两个工具的完整实现、边界行为与扩展思路,能够灵活处理"把对象摊平成数组"或"把数组收敛回对象"这类高频数据处理需求。

为什么需要把对象映射成数组

JavaScript 对象本质上是键值对(key-value pair)的集合,而数组则是有序的元素序列。在真实业务中,二者经常需要互相转换:

  • 后端接口返回的是字典型对象,前端组件却希望以数组形式遍历渲染;
  • 需要把对象的键作为新对象的一个字段(例如把{ 'John': { age: 42 } }变成[{ name: 'John', age: 42 }]);
  • 反之,从表单、列表等数组形态的数据中重建字典型结构。

30 seconds of code 中的listify片段正是为这类场景设计的:用一个自定义映射函数,把对象的每个键值对转换成数组中的一个元素。

listify:把对象映射为对象数组

核心实现

最简单获取键值对的方式是Object.entries(),它返回一个形如[[key, value], ...]的二维数组。接着用Array.prototype.map()结合映射函数,即可把每个键值对转换成目标对象:

const listify = (obj, mapFn) => Object.entries(obj).map(([key, value]) => mapFn(key, value)); const people = [ { name: 'John', age: 42 }, { name: 'Adam', age: 39 }, ]; listify(people, (key, value) => ({ name: key, ...value })); // [ { name: 'John', age: 42 }, { name: 'Adam', age: 39 } ]

逐行拆解

  1. Object.entries(obj):把对象展开为键值对数组。注意上面示例传入的people是一个数组,此时Object.entries()会以索引作为键('0'、'1'),以元素作为值,因此示例能正确把每个对象摊平成{ name: key, ...value };
  2. [key, value]:在map回调中使用数组解构,直接取出键和值;
  3. mapFn(key, value):映射函数被调用时收到两个参数——键和值,其返回值会作为结果数组的元素;
  4. 展开运算符...value:把原对象的属性合并进新对象,name字段则由键提供。

映射函数的自由度

由于mapFn完全由调用方控制,listify可以产出任意形状的结果。例如只保留键:

listify({ a: 1, b: 2 }, key => key); // ['a', 'b']

或者把键值对重新打包为元组:

listify({ a: 1, b: 2 }, (key, value) => [key, value]); // [['a', 1], ['b', 2]]

这种"映射函数决定输出形状"的设计,使listify成为一个可复用的通用工具,而不是只能处理单一业务形态的硬编码函数。

delistify:把对象数组收敛回对象

核心实现

与listify相反,delistify使用Object.fromEntries()把「键值对数组」重新组装为对象。此时映射函数必须返回一个二元数组,其中第一个元素作为键、第二个元素作为值:

const delistify = (arr, mapFn) => Object.fromEntries(arr.map(mapFn)); const people = [ { name: 'John', age: 42 }, { name: 'Adam', age: 39 }, ]; delistify(people, (obj) => { const { name, ...rest } = obj; return [name, rest]; }); // { John: { age: 42 }, Adam: { age: 39 } }

映射函数的返回值约定

delistify的mapFn与listify的有一个关键差异:它必须返回长度为 2 的数组(二元组)。上例中:

  1. const { name, ...rest } = obj;:解构出name字段作为新键,其余属性收集进rest;
  2. return [name, rest];:返回[键, 值]二元组,交给Object.fromEntries()组装。

仓库中另一个片段 map-array-to-object 也遵循同一约定——它用Array.prototype.map()把数组元素映射为二元组,再交给Object.fromEntries()生成对象,印证了"fromEntries接受二元组数组"这一通用模式:

const mapObject = (arr, fn) => Object.fromEntries(arr.map((el, i, arr) => [el, fn(el, i, arr)])); mapObject([1, 2, 3], a => a * a); // { 1: 1, 2: 4, 3: 9 }

一对互补的底层 API

listify与delistify之所以能实现"互为逆操作",根源在于 JavaScript 原生 API 的设计恰好对称:

API输入输出在本文中的角色
Object.entries(obj)对象[[key, value], ...]二维数组listify的起点
Object.fromEntries(pairs)[[key, value], ...]二维数组对象delistify的终点
Array.prototype.map(fn)数组新数组两者的转换载体

更直观地说:Object.entries()相当于"把对象列表化",Object.fromEntries()相当于"把列表对象化",二者天然可逆。仓库中的 convert-map-to-object 片段也利用了这对 API 在Map与普通对象之间互相转换,可见其应用范围远不止对象与数组:

const mapToObject = map => Object.fromEntries(map.entries()); const objectToMap = obj => new Map(Object.entries(obj));

实战:常见数据形态转换

场景一:字典对象 → 带键字段的对象数组

接口返回{ 'apple': { price: 5 }, 'banana': { price: 3 } },需要渲染成表格行:

const inventory = { apple: { price: 5 }, banana: { price: 3 } }; listify(inventory, (key, value) => ({ name: key, ...value })); // [ { name: 'apple', price: 5 }, { name: 'banana', price: 3 } ]

场景二:对象数组 → 以某字段为键的字典

前端表单提交的重复行数据,需要按主键去重或建索引:

const rows = [ { id: 'a1', count: 2 }, { id: 'b2', count: 5 }, ]; delistify(rows, ({ id, ...rest }) => [id, rest]); // { a1: { count: 2 }, b2: { count: 5 } }

场景三:与键变换/键值反转配合使用

对象转换在仓库中是一个完整家族,listify常常与它们组合出现:

  • transform-object-keys 展示了如何用Object.keys()+reduce()映射、重命名甚至递归深层次变换对象键;
  • invert-key-values 用Object.entries()+Array.prototype.map()把键值对反转后交给Object.fromEntries()重建对象,与listify使用完全相同的 API 链路:
    const invertKeyValues = obj => Object.fromEntries(Object.entries(obj).map(entry => entry.reverse())); invertKeyValues({ a: 1, b: 2, c: 3 }); // { 1: 'a', 2: 'b', 3: 'c' }
  • object-array-proxy 则展示了更高阶的思路——用Proxy把对象包装成拥有map、filter、length、Symbol.iterator等数组行为的"键控数组",满足"不想修改原对象又想要数组式 API"的诉求。

可以看到,无论工具形态如何变化,底层都离不开Object.entries()/Object.keys()/Object.values()与Array.prototype.map()的组合。

边界与注意事项

  1. 键的类型会变成字符串:Object.entries()产出的键恒为字符串(数字索引也会被字符串化),因此listify展开数组时得到的是'0'、'1'这样的键名;
  2. 重复键会被覆盖:delistify依赖Object.fromEntries(),如果映射出的键重复,后者会覆盖前者。若需要保留重复键,可参照 invert-key-values 中"遇到重复值收集进数组"的思路;
  3. 映射函数签名要对称:listify的mapFn(key, value)收到两个参数,delistify的mapFn(obj)收到一个对象且必须返回二元组。二者签名不同,混用时务必注意;
  4. 空对象/空数组:Object.entries({})返回空数组,map后仍是空数组;Object.fromEntries([])返回{},两个工具对空输入均能优雅降级,不会报错。

小结

listify与delistify是 30 seconds of code 中一对小巧而对称的工具:前者用Object.entries()+Array.prototype.map()把字典型对象摊平为对象数组,后者用Array.prototype.map()+Object.fromEntries()把对象数组收敛回字典。核心要点可以概括为三句话:

  • 展开:Object.entries()得到键值对数组,map自定义输出形状;
  • 收敛:映射函数返回[key, value]二元组,交给Object.fromEntries()重建对象;
  • 扩展:这对 API 还可用于Map转换、键值反转、键变换等更多对象操作,是 JavaScript 对象处理工具箱中的基础件。

完整的源码与示例位于 content/snippets/js/s/listify.md,可结合 map-array-to-object、convert-map-to-object、transform-object-keys 等相邻片段进一步研读。

  • 教程
  • 文档

【免费下载链接】30-seconds-of-code

Coding articles to level up your development skills

项目地址:https://gitcode.com/gh_mirrors/30/30-seconds-of-code
点击查看免费下载
上一篇:retry-go 快速入门:5分钟学会在 Go 项目中实现优雅重试
下一篇:3分钟掌握Video2X:免费AI视频放大与帧率提升完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询