0

I have an array of objects called records with more than 100 fields, and I need to map every one to create an array called products to add an string. How should I proceed to increase the performance?

Thanks

for (var i = 0; i < records.length; i++) {
  var products = []
  if (records[i].Custom_BA1__c === 'true'){ products.push('Custom BA1')}
  if (records[i].Custom_Web_Sim__c === 'true'){ products.push('Custom Web Sim')}
  if (records[i].Finance_for_Non_Financials__c === 'true'){ products.push('Finance for Non Financials')}
  if (records[i].Custom_Board__c === 'true'){ products.push('Custom Board')}
  ...
}
  • Investigate web workers...spawn 4 - 8 sub processes and split the processing over those 4 - 8 subprocesses. – Randy Casburn May 18 '18 at 16:23
  • Do they match more than one? – epascarello May 18 '18 at 16:24
  • 1
    How long is `records` realistically? – ASDFGerte May 18 '18 at 16:33
  • `native for loop` is the fastest, you can cache the length. [refer](https://stackoverflow.com/questions/5349425/whats-the-fastest-way-to-loop-through-an-array-in-javascript) – Sagar May 18 '18 at 19:20
  • @Sagar That's irrelevant, the micro-optimization you can get from that may even be harmful. – doubleOrt May 18 '18 at 22:20
  • Could you be more clear ? `records` is an array of objects with hundreds of elements, each of these elements has many properties (how many ?) that you need to map to certain strings which you need to push to an array called `products`, and then you save the `products` array from each iteration (by save, I mean you add it to an object outside the loop so you can perform some operations on it after the loop has executed) ? It would be great if you could post your entire code in here. – doubleOrt May 18 '18 at 22:25

3 Answers3

0

// Data from somewhere else
var records = [{
    "Custom_BA1__c": "true",
    "Custom_Web_Sim__c": "false",
    "Finance_for_Non_Financials__c": "true"
}];


var mapping = {
    "Custom_BA1__c": "Custom BA1",
    "Custom_Web_Sim__c": "Custom Web Sim",
    "Finance_for_Non_Financials__c": "Finance for Non Financials"
};

for(let record of records){
    let products = Object.entries(record)
                   .filter(([k,v]) => v === "true")  // filter the ones that are "true"
                   .map(([k,v]) => mapping[k]);      // map them to the correct names
    console.log(products);
}
Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
  • 1
    I don't see how any of these answers are more performant than the OP's code. I rather see them be worse, performance wise, which is the main aspect of the question from how i see it. – ASDFGerte May 18 '18 at 16:36
  • @ASDFGerte This is more efficient because it avoids unnecessary function calls and array manipulations. Dealing this functionally can greatly increase the perfomance. – Derek 朕會功夫 May 18 '18 at 16:39
  • This will be remarkably slower than a for() loop. I highly recommend the OP put this code to the test against their own using jsPerf to see for themselves. – Randy Casburn May 18 '18 at 16:43
  • 1
    @RandyCasburn There is a reason why `.map` is preferred over repeatedly calling `.push`. Your response is equally silly without any support. – Derek 朕會功夫 May 18 '18 at 16:49
  • 2
    JSPerf would, in this case, probably be helpful (unlike the thousands of bogus JSPerf performance topics on this site), but **only** if you actually use a larger dataset. I don't want to derail the topic into "why JSPerf is to be treated with extreme caution". OP has not yet answered how large `records` actually is (maybe this is just a Knuth, "premature optimization is the root of all evil", you know). – ASDFGerte May 18 '18 at 16:50
  • Forget JSPerf then - use the micro timers in the browser with the actual dataset. Either way, it has been proven over and over that the traditional for() loop is the most performant loop in the runtime. – Randy Casburn May 18 '18 at 16:53
  • @RandyCasburn I doubt that would be reliable at all, see https://calendar.perfplanet.com/2010/bulletproof-javascript-benchmarks/. – doubleOrt May 18 '18 at 22:29
  • @elchiconube As ASDFgerte said, please elaborate further. – doubleOrt May 18 '18 at 22:30
  • @doubleOrt - As doubtful as information from a 7 (seven) year old article. Those old articles including from John Resig were the impetus for the Performance API and high precision timing: https://developer.mozilla.org/en-US/docs/Web/API/Performance – Randy Casburn May 19 '18 at 00:07
  • @RandyCasburn See this also: https://github.com/getify/You-Dont-Know-JS/blob/master/async%20%26%20performance/ch6.md – doubleOrt May 19 '18 at 00:08
  • @RandyCasburn what ? – doubleOrt May 19 '18 at 00:10
0

In ES6:

let products = records.map(record => {
  let mapping = {
    'Custom_BA1__c': 'Custom BA1',
    'Custom_Web_Sim__c': 'Custom Web Sim',
    'Finance_for_Non_Financials__c': 'Finance for Non Financials',
    'Custom_Board__c': 'Custom Board'
  }

  return Object.keys(mapping).filter(key => record[key]).map(key => record[key])
}).flatten()
Alex Dovzhanyn
  • 1,028
  • 6
  • 14
  • 1
    This will be remarkably slower than a `for()` loop. I highly recommend the OP put this code to the test against their own using jsPerf to see for themselves. – Randy Casburn May 18 '18 at 16:42
-1

You are going to want to create a binary tree. For each record you will follow a specific path through the binary tree. When you hit the bottom, the tree element will have a single array of strings that you will concat with a single call.

Each tree node looks like this:

{
 var str;
 var strings;
 var left, right;
}

You will populate the tree in two steps.

First time through, at each level you put in the string for that level.

                                                         root
                                 left Node {str: 'Custom BA'}                                right Node {str: ''}
left Node {str: 'Custom Web Sim'}  right Node {str: ''}         left Node {str: 'Custom Web Sim'}  right Node {str: ''}

Second time through, at each level, you will put in the agglomerated array for that level.

                                                         root
                                 left Node {strs: 'Custom BA'}                                             right Node {strings: []}
left Node {strings: ['Custom BA', 'Custom Web Sim']}  right Node {strings: ['Custom BA']}   left Node {strings: ['Custom Web Sim']}  right Node {strings: []}

Now, once the array is populated, given any record, you traverse through the tree to the bottom. At each level, if the given field is 'true' you go left, otherwise you go right. You are basically updating a single node variable to the current node as you go through. Once at the bottom, you call:

products.concat(node.strings)

Why is this faster you ask? Because instead of doing 100 pushes per record, you do 1 concat per record.

Traversing the tree is no slower than the series of if statements you have already, you are just updating a single node variable rather than doing the push with each 'if' statement.

With a small number of records, this may take longer, due to the time taken to populate the tree. But as the record count gets larger, this will be much faster.

The tree will become large, 2 to the power of the number of fields, which for 100 fields is extremely large. So, you might wish to divide this into several trees which are 20 fields per tree (2 to the 20 is about a few megabytes per tree). And then you just traverse each tree individually in sequence.

One other optimization is to skip step 2 of populating the tree, and instead populate as you go along. If you traverse the tree for a record and see at the final node that the "strings" array is not populated, you repeat the same path and populate it.

Sean F
  • 4,344
  • 16
  • 30