2

I have an array of objects with named keys as follows:

Array1 [
  {
    'town1': 'London', // string
    'town2': 'Paris', // string
    'distance': 123456, // number
  },
  {
    'town1': 'Seoul', // string
    'town2': 'Tokio', // string
    'distance': 654321, // number
  },
  {},{},... // Objects with same properties
]

Note that there might be objects without these keys. They should be skipped.
Having all this I want to create a new Array of arrays with 2 objects inside with the following rigid structure:

Array2 [
  [{ name: 'town1', value: 'distance'}, { name: 'town2'}],
  [{ name: 'town1', value: 'distance'}, { name: 'town2'}],
  [{...}, {...}],   // All other objects with existing town1-town2-distance
]

How one could achieve it in the most efficient and fast way?

4
  • 3
    What have you tried so far? Commented Aug 25, 2018 at 14:14
  • Thank you for your question! I know how to create a simple object { name: 'town1', value: 'distance' } by reducing or mapping forEach(a => {}). But I can't figure out how to iterate 2 objects inside array inside another array... Commented Aug 25, 2018 at 14:22
  • 1
    Isn't the output supposed to be [{ name: 'London', value: 123456 }, { name: 'Paris' }]? Commented Aug 25, 2018 at 14:27
  • Yep! And these values would vary depending on objects inside Array1. Commented Aug 25, 2018 at 14:29

4 Answers 4

2

MinusFour's solution is almost correct. You'd also want to filter the elements which actually have the keys required:

Array2.filter(obj => obj.hasOwnProperty("town1") &&
                     obj.hasOwnProperty("town2") &&
                     obj.hasOwnProperty("distance"))
      .map(obj => [{ name: obj.town1, value: obj.distance }, { name: obj.town2 }]);

Is that what you're looking for?

Sign up to request clarification or add additional context in comments.

Comments

2

Looks like a single map would work for you.

let mustHave = ['town1', 'town2', 'distance'];
Array1.filter(obj => mustHave.every(key => obj.hasOwnProperty(key))
      .map(obj => [{ name : obj.town1, value: obj.distance}, { name : obj.town2 }])

Edit: Didn't see that you didn't want to map objects without those keys. The filter takes care of that.

2 Comments

it would, but would throw undefined if any of those keys would be missing. OP wants those objects to be skipped
@PatrykCieszkowski didn't see it, fixed.
2

Just one iteration ( O(n) ) using for. It's faster than filter and map (2 iterations) see this https://jsperf.com/mapfiltervsfor/1

var array1 = [
    {
      'town1': 'London', // string
      'town2': 'Paris', // string
      'distance': 123456, // number
    },
    {
      'town1': 'Seoul', // string
      'town2': 'Tokio', // string
      'distance': 654321, // number
    }
  ];

  var array2 = [];

  for(let i = 0; i<array1.length; i++){

     if(array1[i].hasOwnProperty('town1') &&  array1[i].hasOwnProperty('town2') && array1[i].hasOwnProperty('distance')){
        array2.push([{name:array1[i]['town1'], value: array1[i]['distance']}, { name: array1[i]['town2']}])
     }

  }
  console.log(array2)

2 Comments

First, interesting! That was unexpected I should say. Thank you for separate concerns about performance and the link to that site I didn't know about! I prefer this approach!
@Igniter Thanks, as a general rule (maybe not always), native methods like .map .sort, .filter.. etc. are faster than plain for but sometimes those methods doesn't resolve the whole issue, and we have to use them more than once, in those cases is preferably in my opinion, to use for if we can resolve the issue with less iterations .
1

Ok, I want to have my try as well! Why did nobody think about reduce? It's faster than for-loop! And more elegant. IMO at least.

So I've done few tests, and here are results:

enter image description here

And heres the code:

1) Array.prototype.reduce() with Array.prototype.push()

const array2 = array1.reduce((arr, obj) => {
  if (obj.hasOwnProperty('town1') && obj.hasOwnProperty('town2') && obj.hasOwnProperty('value')) {
    arr.push([ { name: obj.town1, value: obj.distance }, { name: obj.town2 } ])
  }

  return arr
}, [])

2) Array.prototype.reduce() with Array.prototype.concat()

const array2 = array1.reduce((arr, obj) => (
  (obj.hasOwnProperty('town1') && obj.hasOwnProperty('town2') && obj.hasOwnProperty('value'))
    ? Array.prototype.concat(arr, [[ { name: obj.town1, value: obj.distance }, { name: obj.town2 } ]])
    : arr
  ), [])

3) Array.prototype.reduce() with spread operator

array2 = array1.reduce((arr, obj) => (
  (obj.hasOwnProperty('town1') && obj.hasOwnProperty('town2') && obj.hasOwnProperty('value'))
    ?  [...arr, [ { name: obj.town1, value: obj.distance }, { name: obj.town2 } ]]
    : arr
  ), [])

4) And @Emueeus go. For-loop with Array.prototype.push()

for (let i = 0; i<array1.length; i++) {
  if (array1[i].hasOwnProperty('town1') && array1[i].hasOwnProperty('town2') && array1[i].hasOwnProperty('distance')) {
    array2.push([{name:array1[i]['town1'], value: array1[i]['distance']}, { name: array1[i]['town2']}])
  }
}

Here's the test: http://jsfiddle.net/4bca0g2u/

5 Comments

Thank you for your answer! Now that's confusing! I'm constantly getting various results using jsperf.com on your 4 tests. Looks like these tests are conducted on my machine, which is not very trustworthy, so there should be kind of remote-like services. If you know any online script measuring tools like Pingdom or Google Speed for websites, please share!
Here it is: jsperf.com/reduces-vs-for-loop. What do you use for testing performance?
@Igniter I wrote a quick performance test. Iterated each of those solutions 100k times, measured their performance with performance.now() and divided the result by the iteration count. I've fired it on my local machine, you might be right, testing it on multiple environments would give a more trustworthy result. Here's the test through jsfiddle.net/4bca0g2u
I agree because you did one iteration, I'm not agree that it is more elegant. :)
@Emeeus oh, you have every right to do so :) Although native functions seem to be more readable than for-loop. At least to me

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.