-1

I'm trying to find all instances of a query within a set of data pulled from my workbook. The query could be found in one of two columns and may exist in one or multiple rows.

The solution I came up with works, I've tested it with multiple cases using Quokka.js before moving my code to the Apps Script IDE.

Once I ran my code with actual operating data I noticed that regex.test() is returning true for an arbitrary amount of rows. I starting adding some logs to debug and for some reason adding an if statement with a log cause the rest of the code to work, which makes no sense.

This is an example of how the data looks in the sheet:

Query 1 Query 2 Title Quantity
0001 000A Title 1 2
0001 000B Title 2 4
0002 000C Title 3 1
0003 000A Title 4 2
0001 000D Title 5 1

All of this data is being stored into a matrix called orders that will look like this:

[
 [0001, '000A', 'Title 1', 2],
 [0001, '000B', 'Title 2', 4],
 ...
]

This is the code that does not work properly:

// @param {Matrix} orders → row of matrix = [supplierID, tracking, ...others]
// @param {String||Integer} query → supplier Id, tracking, etc.
// @return {Array} instances → array of every row index that contains the query
function searchOrdersMatrix(orders, query) {
    query = query.toString();
    const regex = new RegExp(query, 'gi');
    const instances = [];

    for (let n = 0; n < orders.length; ++n) {
        const row = orders[n].join('');
        const rowMatches = regex.test(row);
        if (rowMatches) instances.push(n + 2);
    }

    return instances;
}

This is the does that does work properly:

// @param {Matrix} orders → row of matrix = [supplierID, tracking, ...others]
// @param {String||Integer} query → supplier Id, tracking, etc.
// @return {Array} instances → array of every row index that contains the query
function searchOrdersMatrix(orders, query) {
  query = query.toString();
    const regex = new RegExp(query, 'gi');
    const instances = [];

    for (let n = 0; n < orders.length; n++) {
        const row = orders[n].join('');
        const rowMatches = regex.test(row);
        if (rowMatches) Logger.log(`Row ${n+2} Regex result: ${regex.test(row)}`);
        if (rowMatches) {
           instances.push(n + 2);
           Logger.log(`Pushed row ${n + 2}`)
        }
    }

    return instances;
}

In both cases, regex is going to follow this structure (example query): /0001/gi, and row is going to look like this (again, example data): '0001000ATitle 12.

So there are a few things I don't understand about this:

  1. The first block of code works, but only for some of the rows despite them all containing the query.
  2. The second block of code results in all of the rows being stored, but if I remove the first if statement it returns the same result as the first block of code.
  3. Although the second block of code works, the log in the first if statement looks like this:

Row 2 Regex: false, Pushed row 2

Row 3 Regex: false, Pushed row 3

Row 5 Regex: false, Pushed row 5

Row 6 Regex: false, Pushed row 6

Row 7 Regex: false, Pushed row 7

Why is const rowMatches = regex.test(row); true but ${regex.test(row)} false?

Given my actual data set, my expected output for instances is [2.0, 3.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 13.0, 14.0, 16.0, 17.0].

This is the logged result from the first block of code:first log

and this is the logged result from the second block of code:second log

I had a hard time giving this question a title given that it's not necessarily a problem with regex.test() & the code does technically work, so please let me know if there is a more accurate way to describe this question

kanwoody
  • 11
  • 3

1 Answers1

0

The issue is that you are using a regular expression with the global flag, as those are stateful. When calling RegExp#test, it will update the regular expression object's lastIndex property and the next time, it will only start matching from this lastIndex. This is obviously undesirable when using the same regular expression instance on multiple strings. To remedy this, simply remove the global (g) flag when constructing the RegExp (which is not needed when just checking for a partial match):

const regex = new RegExp(query, 'i');
Unmitigated
  • 76,500
  • 11
  • 62
  • 80
  • Oh I understand, so does the second block of code return all of the matches because the first if statement is resetting the lastIndex to 0? – kanwoody Mar 17 '23 at 00:55