简体   繁体   中英

Create a new array from another array

I have two arrays. The first one contains of places and some information about them. It's a multidimensional array in three steps:

  • Step 1: Category (eg 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... 

The second array contains of search results, ie places that matched a search. What I want to do is to create a new array with the information in the first array, but only for the elements that's in the second array (so in the example above only place [1] ('Karlstad') should be in the new array.

I also want the new array to have a new structure. Instead of categories in the first level I want the places in the first levels (se example below).

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...

What would be the best and easiest way to do this? I guess I should use a for loop, like below (which doesn't work)?

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]];
}

Thanks a lot in advance!

To add to @Levi's answer, you can also use ES5 map:

Using native Array.prototype.map:

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

Using hand-made map:

Thanks to Marcus' comment below for pointing out performance issues with native map.

Alternative to this is, obviously, a custom version of map that masks the for loop. So far, it seems to do insignificantly better than simply using for loop, but I have no explanation as to why, nor does it really matter.

// 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]
    ];
});

Obviously, the code is still as clean as using native map, but with a ten-fold performance improvement. It's also marginally faster than just a for loop.

Here is a fiddle that executes 1M iterations using various methods:

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

EDIT:

Incidentally, for browsers that are so old they don't even have Array.prototype.map , you can use the ES5 shim to add this feature.

EDIT2:

Check out the documentation for Array.prototype.map on MDN.

EDIT3:

Ok, custom map is not conclusively faster than for-loop, so let's say it performs just as well.

EDIT4:

Final version of the custom map makes it perform consistently faster than the usual for loop which increments the counter. I assume that a for loop that decrements the counter would perform just as well as the final custom map function.

The for loop is probably the easiest given the multi-dimensional structure you have. You just need to declare your variables, and add a little shorthand for easier to read code. This should do:

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]
    ];
}

Since you tagged this with jQuery, here is how you can use jQuery.each to make your code a little simpler (but not as performant > 90% slower as mentioned by @Marcus Ekwall):

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

Keep performance in mind when it matters. jQuery is very convenient and allows you to write code much quicker/easier, but if you are not careful, it can really slow down your site/app.

How about this:

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);

At least this way you're not limited to however big your arrays are.

I suspect the reason why you may be having difficulty with this lies in the fact that you are misusing arrays. Quite simply, that is the incorrect data structure to use here. The information can be more logically represented by 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"
    }
];​

Then your for loop is trivial:

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

which yields a simple collection of place objects, of the same form as that above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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