简体   繁体   English

将嵌套数组转换为对象

[英]Convert nested array into object

An API I'm talking to returns it's registry in a very odd nested array structure. 我正在谈论的API在一个非常奇怪的嵌套数组结构中返回它的注册表。 I want to convert this monstrosity into an object so my application has easy access to whole objects stored within this output. 我想将这个怪物转换成一个对象,这样我的应用程序就可以轻松访问存储在此输出中的整个对象。

The output the API gives me looks like this: API给我的输出看起来像这样:

[ 
    [ "settings", "autoLogout", "false" ], 
    [ "settings", "autoLogoutMinutes", "60" ], 
    [ "settings", "presets", "true" ], 
    [ "controller", "rs232", "ip", "192.168.1.11" ], 
    [ "controller", "rs232", "name", "NX-22" ], 
    [ "source", "M23836", "slot1", "ip", "192.168.1.30" ]
]

The last value in each array represents the value of an entry, everything before that last one adds up to the key used to save the value. 每个数组中的最后一个值表示条目的值,在最后一个条目之前的所有内容都与用于保存值的键相加。 Because of size limitations I can't just drop big json-encoded objects in there, so thats not a viable workaround. 由于大小限制,我不能只在那里删除大的json编码对象,所以这不是一个可行的解决方法。

I've now made a pretty dirty and slow solution involving 2 eval()'s. 我现在做了一个非常肮脏和缓慢的解决方案,涉及2个eval()。 (I know... that's a no-no so I'm looking for a better solution) I'm guessing this can be done loads faster, but I can't figure out how... (我知道......这是一个禁忌,所以我正在寻找一个更好的解决方案)我猜这可以加载更快,但我无法弄清楚如何...

The snippet below uses angular because my application is Angular based, but I'm open to any fast/clean solution. 下面的代码段使用angular,因为我的应用程序是基于Angular的,但我对任何快速/干净的解决方案持开放态度。 A vanilla js approach or some clever use of something like lodash or underscore would be very welcome. 一个香草js方法或一些巧妙使用像lodash或下划线的东西将是非常受欢迎的。

My dirty and slow solution now 我的肮脏和缓慢的解决方案

 function DemoCtrl($scope){ $scope.data = [ [ "settings", "autoLogout", "false" ], [ "settings", "autoLogoutMinutes", "60" ], [ "settings", "presets", "true" ], [ "controller", "rs232", "ip", "192.168.1.11" ], [ "controller", "rs232", "name", "NX-22" ], [ "source", "M23836", "slot1", "ip", "192.168.1.30" ] ] $scope.init = function(){ var registry = {}; angular.forEach($scope.data, function(entry){ var keys = ''; entry.forEach(function(value, key, entry){ if( key != entry.length - 1 ){ //not last of array, so must be a key keys += '[\\'' + value + '\\']'; // check if the object already exists if( !angular.isDefined( eval('registry' + keys) ) ){ eval('registry' + keys + ' = {}'); } }else{ //last one in this entry, must be the value eval('registry' + keys + ' = \\'' + value + '\\''); } }); }); console.log('registry final'); console.log(registry); $scope.registry = registry; } } 
 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <div ng-app> <div ng-controller="DemoCtrl" ng-init="init()"> <pre>{{ registry | json }}</pre> </div> </div> 

Here's a solution that fits your need. 这是一个适合您需求的解决方案。 Also, please, never use eval. 另外,请永远不要使用eval。 There's always a better way in JavaScript. 在JavaScript中总是有更好的方法。

You can adapt the code below to your use case. 您可以根据您的使用情况调整以下代码。

 var data = [ [ "settings", "autoLogout", "false" ], [ "settings", "autoLogoutMinutes", "60" ], [ "settings", "presets", "true" ], [ "controller", "rs232", "ip", "192.168.1.11" ], [ "controller", "rs232", "name", "NX-22" ], [ "source", "M23836", "slot1", "ip", "192.168.1.30" ] ]; var o = {}; data.forEach(function(a) { var keys = a.slice(0, a.length-2); var cur = o; keys.forEach(function(k) { if (cur[k] == null) { cur[k] = {}; } cur = cur[k]; }); cur[a[a.length-2]] = a[a.length-1] }); output.innerHTML = JSON.stringify(o, null, 2); 
 <pre id='output'></pre> 

A compact solution which avoids the calculation of the value position in the array. 一种紧凑的解决方案,可避免计算阵列中的值位置。

 var array = [ ["settings", "autoLogout", "false"], ["settings", "autoLogoutMinutes", "60"], ["settings", "presets", "true"], ["controller", "rs232", "ip", "192.168.1.11"], ["controller", "rs232", "name", "NX-22"], ["source", "M23836", "slot1", "ip", "192.168.1.30"] ], obj = {}; array.forEach(function (a) { var p = obj, v = a.pop(), k = a.reduce(function (r, b) { p[r] = p[r] || {}; p = p[r]; return b; }); p[k] = v; }); document.write('<pre>' + JSON.stringify(obj, 0, 4) + '</pre>'); 

Basically you just have loop over them and create nested objects. 基本上你只是循环它们并创建嵌套对象。 You don't need to use eval for this. 您不需要为此使用eval。 There are a lot of reasons why you shouldn't use it. 你不应该使用它的原因有很多。 Performance, security, debuggability ( https://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/ ) 性能,安全性,可调试性( https://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/

var asObject = {}
//loop over them
data.forEach(function(val) {
    //create the top level object that matches the key if it doesn't exist
   if (!asObject.hasOwnProperty(val[0])) {
    asObject[val[0]] = {};
   }
   //store it 
   var theHolder = asObject[val[0]];
   //loop over all the middle elements creating nested object 
   for (var index = 1; index < val.length - 2; index++) {
       var element = val[index];
       if (!theHolder.hasOwnProperty[element]) {
           theHolder[element] = {};
       } 
       theHolder = theHolder[element]
   }
    //the last one is the value, so just set it
    var lastKey = val[val.length - 2];
    theHolder[lastKey] = val[val.length - 1];
});

console.log(asObject);
var someObj = $scope.data.reduce(function(accum, array) {
    var value = array.pop(); //pulls last item off of array

    //takes the remaining items and condenses them into 1 string
    var key = array.reduce(function(acc, str) {
        return acc + str;
    }, '');

    accum[key] = value;
    return accum;
}, {}); //the empty object in this line is the seed value

Every sub-array gets the treatment and passed into the empty object seed which is then assigned to someObj . 每个子数组都获得处理并传递给空对象种子,然后将其分配给someObj

 function DemoCtrl($scope){ $scope.data = [ [ "settings", "autoLogout", "false" ], [ "settings", "autoLogoutMinutes", "60" ], [ "settings", "presets", "true" ], [ "controller", "rs232", "ip", "192.168.1.11" ], [ "controller", "rs232", "name", "NX-22" ], [ "source", "M23836", "slot1", "ip", "192.168.1.30" ] ] $scope.init = function(){ var registry = {}; angular.forEach($scope.data, function(entry) { var len = entry.length, tmp = registry; for (var i = 0; i < len - 1; i++) { key = entry[i]; if (i < len - 2) { if (!tmp[key]) { tmp[key] = { }; } tmp = tmp[key]; } else { tmp[key] = entry[i + 1]; } } }); console.log('registry final'); $scope.registry = registry; } } 
 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <div ng-app> <div ng-controller="DemoCtrl" ng-init="init()"> {{ registry }} </div> </div> 

Here it is done using recursion: 这是使用递归完成的:

$scope.registry = $scope.data.reduce(function register(registry, entry) {
    var key = entry[0];
    if (entry.length === 2) {
        registry[key] = entry[1];
    } else {
        registry[key] = register(registry[key] || {}, entry.slice(1));
    }
    return registry;
}, {});

Here is another option based on @Jared Smith's solution above. 这是基于@Jared Smith上述解决方案的另一种选择。 In his solution the keys were concatenated into string keys in a shallow map. 在他的解决方案中,键被嵌入到浅地图中的字符串键中。 This creates the nested object structure of my other solution. 这将创建我的其他解决方案的嵌套对象结构。

If you're new to array.reduce(), see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce 如果您是array.reduce()的新手,请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

var someObj = array.reduce(function(previousVal, currentVal) {
    //strip off the value to use at the end
    var value = currentVal.pop();

    //create all the nested objects
    currentVal.reduce(function(acc, str, idx, arr) {

        if (idx !== arr.length - 1 ) {
            if (!acc.hasOwnProperty(str)) {
                acc[str] = {};
            }
            return acc[str];    
        } else {
            //the last one in the array is the key for the value
            acc[str] = value;
            return;
        }

    }, previousVal);
    return previousVal;
}, {}); 

console.log(someObj);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM