Jim
Jim

Reputation: 658

More efficient way search for multiple objects in array

I am currently using a for loop and multiple if statements to count the number of records that match a string.

for (var i = 0; i < DailyLogs.length; i++) {
  if (DailyLogs[i].created_at >= this.state.startMonth && DailyLogs[i].created_at <= this.state.finishMonth) {
    if (DailyLogs[i].created_by_id === "37aa0778-c148-4c04-b239-18885d46a8b0" ) { md1++; }
    if (DailyLogs[i].created_by_id === "869a7967-ffb3-4a20-b402-ad6d514472de" ) { md2++; }
    if (DailyLogs[i].created_by_id === "92c0f155-ce82-4b50-821f-439428c517a3" ) { md3++; }
    if (DailyLogs[i].created_by_id === "aa9eb0f2-35af-469a-8893-fc777b444bed" ) { md4++; }
    if (DailyLogs[i].created_by_id === "967d63ea-492c-4475-8b08-911be2d0bf22" ) { md5++; }
    if (DailyLogs[i].created_by_id === "47ec8d60-1fa2-4bf5-abc8-34df6bd53079" ) { md6++; }
    if (DailyLogs[i].created_by_id === "92c0f155-ce82-4b50-821f-439428c517a3" ) { md7++; }
  }
}

Is there a better way to do this, apart from having multiple if statements, or perhaps shorting this somehow.

Upvotes: 1

Views: 74

Answers (4)

T.J. Crowder
T.J. Crowder

Reputation: 1074495

You can make it (slightly) more efficient by using else if, since if an earlier if's condition is true, by definition later ones can't be.

Long series of else if in JavaScript can be written as switch instead.

for (var i = 0; i < DailyLogs.length; i++) {
  if (DailyLogs[i].created_at >= this.state.startMonth && DailyLogs[i].created_at <= this.state.finishMonth) {
      switch (DailyLogs[i].created_by_id) {
        case "37aa0778-c148-4c04-b239-18885d46a8b0": md1++; break;
        case "869a7967-ffb3-4a20-b402-ad6d514472de": md2++; break;
        case "92c0f155-ce82-4b50-821f-439428c517a3": md3++; break;
        case "aa9eb0f2-35af-469a-8893-fc777b444bed": md4++; break;
        case "967d63ea-492c-4475-8b08-911be2d0bf22": md5++; break;
        case "47ec8d60-1fa2-4bf5-abc8-34df6bd53079": md6++; break;
        case "92c0f155-ce82-4b50-821f-439428c517a3": md7++; break;
      }
  }
}

Using else if vs. switch is largely a matter of style in cases like this.

Another option is to maintain your counters in an object keyed by the created_by_id:

var md = {
    "37aa0778-c148-4c04-b239-18885d46a8b0": 0,
    "869a7967-ffb3-4a20-b402-ad6d514472de": 0,
    "92c0f155-ce82-4b50-821f-439428c517a3": 0,
    "aa9eb0f2-35af-469a-8893-fc777b444bed": 0,
    "967d63ea-492c-4475-8b08-911be2d0bf22": 0,
    "47ec8d60-1fa2-4bf5-abc8-34df6bd53079": 0,
    "92c0f155-ce82-4b50-821f-439428c517a3": 0
};
for (var i = 0; i < DailyLogs.length; i++) {
  if (DailyLogs[i].created_at >= this.state.startMonth && DailyLogs[i].created_at <= this.state.finishMonth && md.hasOwnProperty(DailyLogs[i].created_by_id)) {
      md[DailyLogs[i].created_by_id]++;
  }
}

You can also avoid the repeated DailyLogs[i] by using a variable in the loop body:

var log = DailyLogs[i];
if (log.created_at >= ...)

In modern ES2015+ environments, you can combine for-of with destructuring:

for (const {created_at, created_by_id} of DailyLogs) {
  if (created_at >= this.state.startMonth && created_at <= this.state.finishMonth) {
      switch (created_by_id) {
        case "37aa0778-c148-4c04-b239-18885d46a8b0": md1++; break;
        case "869a7967-ffb3-4a20-b402-ad6d514472de": md2++; break;
        case "92c0f155-ce82-4b50-821f-439428c517a3": md3++; break;
        case "aa9eb0f2-35af-469a-8893-fc777b444bed": md4++; break;
        case "967d63ea-492c-4475-8b08-911be2d0bf22": md5++; break;
        case "47ec8d60-1fa2-4bf5-abc8-34df6bd53079": md6++; break;
        case "92c0f155-ce82-4b50-821f-439428c517a3": md7++; break;
      }
  }
}

Those options can be combined in various ways, for instance:

const md = {
    "37aa0778-c148-4c04-b239-18885d46a8b0": 0,
    "869a7967-ffb3-4a20-b402-ad6d514472de": 0,
    "92c0f155-ce82-4b50-821f-439428c517a3": 0,
    "aa9eb0f2-35af-469a-8893-fc777b444bed": 0,
    "967d63ea-492c-4475-8b08-911be2d0bf22": 0,
    "47ec8d60-1fa2-4bf5-abc8-34df6bd53079": 0,
    "92c0f155-ce82-4b50-821f-439428c517a3": 0
};
for (const {created_at, created_by_id} of DailyLogs) {
  if (created_at >= this.state.startMonth && created_at <= this.state.finishMonth && md.hasOwnProperty(created_by_id)) {
      md[created_by_id]++;
  }
}

Upvotes: 4

JanS
JanS

Reputation: 2075

Assuming you are merely counting, I would reccommend a map. This makes it easier to add more ids.

var counter = {
  '37aa0778-c148-4c04-b239-18885d46a8b0': 0,
  '869a7967-ffb3-4a20-b402-ad6d514472de': 0
}

function count(logs) {
  logs.filter(function(entry) {
    return entry.created_at >= this.state.startMonth &&
           entry.created_at <= this.state.finishMonth && 
           counter.hasOwnProperty(entry.created_by_id)
  }).forEach(function(entry) {
    counter[entry.created_by_id]++;
  })
}

Upvotes: 2

Nina Scholz
Nina Scholz

Reputation: 386654

You could store the id to check against in an object and update the the count.

var ids = {
        "37aa0778-c148-4c04-b239-18885d46a8b0": 0,
        "869a7967-ffb3-4a20-b402-ad6d514472de": 0,
        "92c0f155-ce82-4b50-821f-439428c517a3": 0,
        "aa9eb0f2-35af-469a-8893-fc777b444bed": 0,
        "967d63ea-492c-4475-8b08-911be2d0bf22": 0,
        "47ec8d60-1fa2-4bf5-abc8-34df6bd53079": 0,
        "92c0f155-ce82-4b50-821f-439428c517a3": 0
    };

DailyLogs.forEach(({ created_at: at, created_by_id: id }) => {
    if (at >= this.state.startMonth && at <= this.state.finishMonth && id in ids) {
        ids[id]++;
    }
});

Upvotes: 1

Barmar
Barmar

Reputation: 781210

Any time you find yourself creating variables in numeric sequence you should probably be using an array. Or in this case, you could use an object whose keys are the IDs you want to match.

var md = {
    "37aa0778-c148-4c04-b239-18885d46a8b0": 0,
    "869a7967-ffb3-4a20-b402-ad6d514472de": 0,
    ...
};
DailyLogs.forEach(l => 
    l.created_at >= this.state.startMonth && 
    l.created_at <= this.state.finishMonth && l.created_by_id in md && 
    md[l.created_by_id]++
);

Upvotes: 2

Related Questions