您的位置:首页 » 分类: JavaScript & ES2015 (ES6) » 文章: 回归基础:JavaScript 中的数据格式转换一例

回归基础:JavaScript 中的数据格式转换一例

小编推荐:掘金是一个高质量的技术社区,从 ECMAScript 6 到 Vue.js,性能优化到开源类库,让你不错过前端开发的每一个技术干货。各大应用市场搜索「掘金」即可下载APP,技术干货尽在掌握..

常见问题

打个比方,你有一组数据,需要根据拼音首字母字母索引显示在页面上,如图:

这种索引性的展示已经随处可见。对于我们前端开发来说这种展示最喜欢的数据格式可能是这样的:

var cardList = [
    {
        "key":"p",
        "cars":[
            {
                "id":"888888888888888888",
                "name":"帕加尼",
                "logo":"http://...",
                "initial":"p" 
            }
        ]
    },    
    {
        "key":"q",
        "cars":[
            {
                "id":"888888888888888889",
                "name":"起亚",
                "logo":"http://...",
                "initial":"q" 
            },
            {
                "id":"888888888888888890",
                "name":"奇瑞",
                "logo":"http://...",
                "initial":"q" 
            },
            ...
        ]
    },
    ...
];

或者这样:

var cars = {
    "p":[
            {
                "id":"888888888888888888",
                "name":"帕加尼",
                "logo":"http://...",
                "initial":"p" 
            }
    ],
    "q":[
            {
                "id":"888888888888888889",
                "name":"起亚",
                "logo":"http://...",
                "initial":"q" 
            },{
                "id":"888888888888888890",
                "name":"奇瑞",
                "logo":"http://...",
                "initial":"q" 
            },
            ...
    ]
    ...
};

相信对于大多数的前端来说,这样的数据去格式化模板都是非常受欢迎的。相比之下,对于上面的例子来说,第一种数据格式更加方便前端大多数的模板使用,而第二种方式更加直观。然而,我们后端的同事吐给我们的很可能是这样的数据格式:

var cars = [
    {
        "id":"888888888888888888",
        "name":"帕加尼",
        "logo":"http://...",
        "initial":"p" 
    },{
        "id":"888888888888888889",
        "name":"起亚",
        "logo":"http://...",
        "initial":"q" 
    },
    {
        "id":"888888888888888890",
        "name":"奇瑞",
        "logo":"http://...",
        "initial":"q" 
    }
    ...
];

相信大家都多次遇到过类似的问题,除了转换数据格式外我找不到一个真正满意的解决方案。对于前端来说最简单办法可能是叫后端转换成你想要的数据格式。不过这肯定不是这篇文章想要说明的,我们需要自己动手将数据格式转换成我们想要的。根据上面的例子,做直观的做法是将数据按首字母分组,可惜的是 Javascript 的 Array 对象中没有原生的 groupBy() 方法,但我们自己添加一个不是太难。

groupBy()

Javascript 提供了许多其他功能强大的方法,比如 map()filter()sort() 等等。今天我们用一个 ES5 的方法 reduce()

reduce() 方法的工作原理是 将累加器和数组中的每个元素 (从左到右)应用一个函数, 本质上它的目的是将一个数组 “减少” 成一个对象。详细说明可以查看 MDN 文档。下面就用 reduce() 方法来实现 groupBy()

Array.prototype.groupBy = function(prop) {
    return this.reduce(function(groups, item) {
        var val = item[prop].toUpperCase();
        groups[val] = groups[val] || [];
        groups[val].push(item);
        return groups;
    }, {});
}

这里来稍微解析一下:

Array.prototype.groupBy = function(prop) {
  ...
}

首先,我们在 Array 的原型对象上添加一个 groupBy 方法,这意味我们可以在任何 Array 对象上调用这个方法,类似这样:.groupBy(prop) 。其中 prop 参数使我们想要分组的属性名。当然如果你觉得这样做会污染 Array 的原型对象的话,你可以将其改写成工具函数:

function groupBy (arr,prop){
    return arr.reduce(function(groups, item) {
        var val = item[prop].toUpperCase();
        groups[val] = groups[val] || [];
        groups[val].push(item);
        return groups;
  }, {});
}

接下来我们看方法内部实现。

return this.reduce(function(groups, item) {
  ...
}, {});

我们在需要处理的数组上调用 reduce() 方法,并传入两个参数,第一个是 callback ,我们要在数组中的每个元素上运行的函数,第二个是累加器,本质上说这个参数是用于第一次调用 callback 函数的第一个参数。因为我们需要得到是一个对象,为此,我们使用一个空对象 {}

reduce()方法将遍历数组中的每个元素,通过传入累加器和当前元素调用我们的 callback 函数。该 callback 函数的工作是找出将元素放在累加器中的位置,然后返回更新的累加器,具体实现:

var val = item[prop].toUpperCase();
groups[val] = groups[val] || [];
groups[val].push(item);
return groups;

我们希望根据给定属性的值对所有数组元素进行分组。所以如果该属性是 initial ,那么在开始的例子中,起亚和奇瑞应该在一个分组中,因为他们的首字母都是q

  • 第 1 行:将获取当前数组元素给定属性的属性值,为了展示需要,使用 toUpperCase() 将其转换为大写字母。
  • 第 2 行:是检查我们是否已经为给定属性的属性值设置了一个新的数组,如果累加器中没有该数组,则添加一个空数组。
  • 第 3 行:将当前数组元素添加到刚刚添加的数组中。
  • 第 4 行:返回更新的累加器,传递给下一次遍历,因此下一个遍历的数组元素可以更新到累加器上。

没几行代码,就可以帮我们完整数据格式转换。

示例

回调最开始的例子,我们来验证一下我们的 groupBy() 方法。

Array.prototype.groupBy = function(prop) {
    return this.reduce(function(groups, item) {
        var val = item[prop].toUpperCase();
        groups[val] = groups[val] || [];
        groups[val].push(item);
        return groups;
    }, {});
}
var cars = [
    {
        "id":"888888888888888888",
        "name":"帕加尼",
        "logo":"http://...",
        "initial":"p" 
    },{
        "id":"888888888888888889",
        "name":"起亚",
        "logo":"http://...",
        "initial":"q" 
    },
    {
        "id":"888888888888888890",
        "name":"奇瑞",
        "logo":"http://...",
        "initial":"q" 
    }
];

var cardGurop = cars.groupBy("initial");

// cardGurop 的结果是:
// {
//     "P":[
//             {
//                 "id":"888888888888888888",
//                 "name":"帕加尼",
//                 "logo":"http://...",
//                 "initial":"p" 
//             }
//     ],
//     "Q":[
//             {
//                 "id":"888888888888888889",
//                 "name":"起亚",
//                 "logo":"http://...",
//                 "initial":"q" 
//             },{
//                 "id":"888888888888888890",
//                 "name":"奇瑞",
//                 "logo":"http://...",
//                 "initial":"q" 
//             }
//     ]
// }

进一步转换数据格式

好了,大功告成?或许这不是我们要的最终结果,实际工作中,以下的数据格式可能更加适用于我们前端模板:

var cardList = [
    {
        "key":"P",
        "cars":[
            {
                "id":"888888888888888888",
                "name":"帕加尼",
                "logo":"http://...",
                "initial":"p" 
            }
        ]
    },    
    {
        "key":"Q",
        "cars":[
            {
                "id":"888888888888888889",
                "name":"起亚",
                "logo":"http://...",
                "initial":"q" 
            },
            {
                "id":"888888888888888890",
                "name":"奇瑞",
                "logo":"http://...",
                "initial":"q" 
            },
            ...
        ]
    },
    ...
];

那么我们可能需要进一步装换一下。思路也很简单,将 groupBy() 方法装换出来的 cardGurop 对象再装换一次:

var keyArr = Object.keys(cardGurop);
var cardList = [];
keyArr.forEach(key => {
    cardList.push({
        "key"  : key,
        "cars" : cardGurop[key]
    })
});

// cardList 的结果是:
// [
//     {
//         "key":"P",
//         "cars":[
//             {
//                 "id":"888888888888888888",
//                 "name":"帕加尼",
//                 "logo":"http://...",
//                 "initial":"p" 
//             }
//         ]
//     },    
//     {
//         "key":"Q",
//         "cars":[
//             {
//                 "id":"888888888888888889",
//                 "name":"起亚",
//                 "logo":"http://...",
//                 "initial":"q" 
//             },
//             {
//                 "id":"888888888888888890",
//                 "name":"奇瑞",
//                 "logo":"http://...",
//                 "initial":"q" 
//             }
//         ]
//     }
// ];

总结

实际工作中,掌握一些数据格式装换的技能非常有用,也非常常见,后端同事对于同一份数据可能只会给你一个接口或一种数据格式。除非你碰到了一个大好人,碰到个较真的会跟你谈人生,谁都希望自己代码的逻辑简单。当然我这里只是给了一个最简单的示例,你可能会碰到数据格式装换或者数据操作更加复杂的情况。学习 Javascript 基础和原生对象的方法非常重要。

当然,你也可以使用一些工具类库来解决问题,比如 LodashUnderscore.js 都包含我们上面说的 groupBy 方法,当然还有很多其他有用的方法。不过对于一些有洁癖的工程师来说,一个简单的小功能,引入一个类库,确实有点不值。

 

正文完。下面还有一个推广让最好的人才遇见更好的机会!

互联网行业的年轻人,他们面对着怎样的职业瓶颈、困惑与未来选择?过去,这鲜有人关心。资深的职场人,也多半优先选择熟人去推荐机会。

100offer致力于改变现状,帮互联网行业最好的人才发现更好的机会。使用 100offer.com 或 100offer App ,可以一周内获得中国、美国等数千家优质企业的工作机会。

马上去遇见更好的机会
推广结束

关注WEB前端开发官方公众号

关注国内外最新最好的前端开发技术干货,获取最新前端开发资讯,致力于打造高质量的前端技术分享公众号

发表评论

电子邮件地址不会被公开。 必填项已用*标注