从另一个数组创建新数组

3

我有两个数组。第一个数组包含地点信息和一些相关信息。这是一个三维的多维数组:

  • Step 1: Category (e.g. header, date etc...).
  • Step 2: The actual place
  • Step 3: Latitude and longitude (under the category coordinates)

    places[0][0] = 'Stockholm';     // Header
    places[0][1] = 'Karlstad';
    places[0][2] = 'Borgholm';
    places[1][0] = '2012-05-25';    // Date
    places[1][1] = '2012-06-12';    
    places[1][2] = '2012-05-14'
    places[2][0] = 'Lorum ipsum lorum ipsum lorum ipsum';       // Description
    places[2][1] = 'Ipsum lorum lorum ipsum lorum ipsum';
    places[2][2] = 'Forum Lorum Porum';
    places[3][0][0] = '56,123342';      // Latitude
    places[3][0][1] = '49,123123';      // Longitude
    places[3][1][0] = '23,543231';
    places[3][1][1] = '45,955432';
    places[3][2][0] = '34,123459';
    places[3][2][1] = '45,325198';
    ...and so on...
    
第二个数组包含搜索结果,即与搜索匹配的地点。我想做的是创建一个新的数组,其中包含第一个数组中的信息,但仅限于第二个数组中的元素(因此在上面的示例中,只有地点[1]('Karlstad')应该在新数组中。
我还希望新数组具有新的结构。我希望第一级别中是地点而不是类别(请参见下面的示例)。
results = [1, 15, 17, 19, 32, 91, 102, 103];

newPlaces[0][0] = 'Karlstad';
newPlaces[1][0] = 'Kalmar';
newPlaces[2][0] = 'Luleå';
newPlaces[3][0] = 'Överkalix';
newPlaces[4][0] = 'Malmö';
newPlaces[5][0] = 'Ystad';
newPlaces[6][0] = 'Linköping'
...and so on...

如何最好且最简单地完成这个任务呢?我想我应该使用 for 循环,就像下面这样(但这并不起作用):

for (var i = 0; i < results.length; i++) {
    newPlaces[i][0] = places[0][results[i]];
    newPlaces[i][1] = places[1][results[i]];
    newPlaces[i][2] = places[2][results[i]];
    newPlaces[i][3] = places[3][results[i]];
}

非常感谢您的预先帮助!


3
你应该真的真的非常使用JSON来处理这种东西,这对机器和人类都会更加容易。你可以轻松地将其转换为JSON格式,反之亦然。 - Hamed Al-Khabaz
1
赞同@Wololo的建议。使用JSON和/或JS对象可以使这种东西更易读。 - user234932
4个回答

4

除了 @Levi 的回答,您还可以使用 ES5 map:

使用原生 Array.prototype.map:

newPlaces = results.map(function(result) {
    return [
        places[0][result],
        places[1][result],
        places[2][result],
        places[3][result]
    ];
});

使用手工制作地图:

感谢Marcus在下面的评论中指出了本机地图性能问题。

替代方案是显然的,即一个自定义版本的地图,可以掩盖for循环。到目前为止,它似乎比简单地使用for循环略微好一些,但我没有解释为什么,也并不重要。

// Custom map function
function map(arr, func) {
    var newArr = [];
    var cnt = arr.length;
    var arrLen = arr.length;
    for (; cnt; cnt--) {
       newArr.push(func(arr[arrLen - cnt]));    
    }
    return newArr;
}

newPlaces = map(results, function(result) {
    return [
        places[0][result],
        places[1][result],
        places[2][result],
        places[3][result]
    ];
});

显然,使用这种方法编写的代码与使用native map方法编写的代码一样简洁,但性能提升了十倍。它的速度也比仅使用for循环略快。

这是一个fiddle示例,可以使用多种方法执行100万次迭代:

http://jsfiddle.net/6wArq/3/

编辑:

顺便提一下,对于那些浏览器太老而没有 Array.prototype.map 的情况,您可以使用 ES5 shim 来添加此功能。

编辑2:

请查看 MDN 上关于 Array.prototype.map文档

编辑3:

好的,自定义 map 函数并不能比 for 循环更快,所以我们说它的性能与 for 循环相同。

编辑4:

最终版本的自定义 map 使其性能始终优于通常的通过增加计数器的 for 循环。我认为通过减少计数器的 for 循环与最终的自定义 map 函数具有同样的性能。


使用.map很好,但与内联for循环相比性能表现非常差(在我的系统上慢了约80%),因此我反对在这种特定情况下使用此方法。 - mekwall
1
@MarcusEkwall:我更新了答案,包括一个自定义版本的map函数,它的性能甚至比for循环还要好。;) - user234932
不错!原生的Array.map比JS实现要慢,这很荒谬。嗯,当引擎优化突破使用C绑定的开销时就会发生这种情况(PCRE也是如此)。遗憾的是,回归测试无法检测到这些性能提升 ;) - mekwall
@MarcusEkwall:是的,我一直认为本地代码应该运行得更快,特别是像 map 这样的东西可以并行化处理。 - user234932
@bvukleic 这个假设是正确的,只要你不计算使用C绑定的开销。这也可能是为什么它们仍然以这种方式实现的原因 :P - mekwall
@MarcusEkwall:天啊!在Chromium中,for循环的速度比最新的Firefox慢了3倍以上(Linux 64位)。这是怎么回事!编辑:在Opera中比FF慢50%。 - user234932

2

考虑到你拥有的多维数据结构,for循环可能是最简单的。你只需要声明变量,并添加一些缩写以使代码更易于阅读。以下代码应该可以胜任:

var newPlaces = [];
for (var i = 0; i < results.length; i++) {
    var id = results[i]
    newPlaces[i] = [
        places[0][id], 
        places[1][id],
        places[2][id],
        places[3][id],
        places[4][id]
    ];
}

既然您在标签中加了jQuery,这里是如何使用jQuery.each使您的代码更简单一些(但不如@Marcus Ekwall所述的性能高> 90%慢):

var newPlaces = [];
$.each(results, function(i, id) {
    newPlaces[i] = [
        places[0][id], 
        places[1][id],
        places[2][id],
        places[3][id],
        places[4][id]
    ];
});

在关键时刻考虑性能。jQuery非常方便,可以让您更快、更轻松地编写代码,但如果不小心使用,它可能会严重拖慢您的网站或应用程序。


3
这是一个关于上述问题的测试案例。由于 @Levi 比我先回答,所以我不会再添加答案。 - mekwall
1
不要将 .each 作为内联 for 循环的替代品。在这种情况下,微小的可读性提高并不值得巨大的性能下降。我们在这里谈论的是大约 90% 的速度下降 :) - mekwall
我完全同意Marcus的观点,在大多数情况下,包括这种情况(因为我们不知道有多少地方需要处理),使用循环通常是更好的选择。然而,当我知道自己正在处理一个小数据集时,方便和可读性对我来说通常是值得的。 - Levi
我相信大多数人缺乏对jQuery等框架的了解,并且只要有可能就使用它们,而不考虑性能。但只要你意识到它的影响,我就不反对。 - mekwall

1
这个怎么样?
var places = [ ];

...

var newPlaces = [ ];
var placesArray;

for (var i = 0; i < places[0].length; i++) // loop through the headers first
{
  placesArray = [ ];

  for (var j = 0; j < places.length; j++) // make sure we loop through the other properties too
  {
    placesArray.push(
      places[j][i]
    );
  }

  newPlaces.push(
    placesArray
  );
}

console.log(places, placesArray);

至少这样你不会受到数组大小的限制。


0
我怀疑你在这方面遇到困难的原因是你误用了数组。简单来说,那不是在这里使用的正确数据结构。这些信息可以更合理地通过JSON表示。
var places = [
    {
        "Header": "Stockholm",
        "Date": "2012-05-25",
        "Description": "Lorum ipsum lorum ipsum lorum ipsum",
        "Latitude": "56,123342",
        "Longitude": "49,123123"
    },
    {
        "Header": "Karlstad",
        "Date": "2012-06-12",
        "Description": "Lorum ipsum lorum ipsum lorum ipsum",
        "Latitude": "23,543231",
        "Longitude": "45,955432"
    }
];​

那么你的for循环就非常简单:

var newPlaces = [];
for (var i = 0; i < results.length; i++) {
    newPlaces[i] = places[results[i]];
}

这将产生一个简单的位置对象集合,与上面的形式相同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接