Search code examples
javascriptarraysstringmappingsanitization

Less resource intensive way of changing a lot of strings in array JS


I'm trying to change a lot of strings and haven't found a "good" way. What I want to do is remove some strings based on different criteria. Exactly:

  1. change 'PD-111 S/A' to 'PD-111' < remove all instances of 'S/A' when the string starts with 'PD-'
  2. change 'KL.OTHERSTUFF' to 'KLOTHERSTUFF' < remove all instances of '.' when the string starts with 'KL'
  3. change 'BD5000-50' to 'BD5000' < remove all instances of '-50' when the string starts with 'BD'

I've tried the .startsWith() and .replace() without any luck and diff iterators like .map to try to create a function to delete based on criteria.

I ended doing a forEach but it's incredibly resource intensive (since the total changes would be around 4k strings). It works but it's really bad I think eventhough I'm a pretty serious noob.

I'll do reproducible code next:

let firstArr = [{
    sku: 'PD-123-A S/A',
    stock: '0'
  },
  {
    sku: 'PD-123-B S/A',
    stock: '1'
  },
  {
    sku: 'PD-123-C S/A',
    stock: '1'
  }
]

let repl = {
  'PD-123-A S/A': 'PD-123-A',
  'PD-123-B S/A': 'PD-123-B',
  'PD-123-C S/A': 'PD-123-C',
}

firstArr.forEach(element => {
  let keys = Object.keys(repl);
  let values = Object.values(repl);
  for (let i = 0; i < keys.length; i++) {
    if (keys[i] === element.sku) {
      element.sku = values[i];
    }
  }
});
console.log(firstArr);


Solution

  • The most viable solution most probably would be based on a single re-usable mapper function which gets the regex lookup (key-value pairs of a string value's start sequence and its regex based replacement pattern) passed as the map method's optional 2nd thisArg parameter.

    The mapping function is implemented generically, thus agnostic to any item property. It operates all of an original item's entries, executes the replacement at just string type values and creates a sanitized shallow copy of the originally processed item. Thus the original data structure does not get mutated.

    function createSanitizedEntriesFromBoundRegMap(item) {
      const regMap = this;
    
      return Object
        .fromEntries(
          Object
            .entries(item)
            .map(([key, value]) => [
              key,
              // target string values only.
              ('string' === typeof value)
                ? value.replace(
                    regMap[value.slice(0, 3)] ??
                    regMap[value.slice(0, 2)] ??
                    '', ''
                  )
                : value
            ])
        );
    }
    
    const sampleArray = [{
      asu: 'BD5000-50',
      sku: 'PD-123-A S/A',
      tvz: 'KL.OTHERSTUFF',
      stock: '0',
    }, {
      tvz: 'KL.EVE.R.STUFF',
      sku: 'PD-123-B S/A',
      stock: '1',
    }, {
      sku: 'PD-123-C S/A',
      asu: 'BD300-5000-5000',
      stock: '1',
    }];
    
    const resultArray = sampleArray
      .map(createSanitizedEntriesFromBoundRegMap, {
        // provide the regex lookup as `thisArg`.
        'PD-': /\s*S\/A\s*/g,
        KL: /\.+/g,
        BD: /-50/g,
      });
    
    console.log({
      resultArray,
      sampleArray,
    });
    .as-console-wrapper { min-height: 100%!important; top: 0; }

    One of the above implementation's core features is the lookup based regex access which supports keys of varying length. The next provided test code demonstrates this solution's main idea.

    const regMap = {
      'PD-': /\s*S\/A\s*/g,
      KL: /\.+/g,
      BD: /-50/g,
    };
    const pdValue = 'PD-123-A S/A';
    const klValue = 'KL.OTHERSTUFF';
    const bdValue = 'BD5000-50';
    
    console.log({
      regXSearch_pdTest:
        regMap[pdValue.slice(0, 3)] ?? regMap[pdValue.slice(0, 2)],
      regXSearch_klTest:
        regMap[klValue.slice(0, 3)] ?? regMap[klValue.slice(0, 2)],
      regXSearch_bdTest:
        regMap[bdValue.slice(0, 3)] ?? regMap[bdValue.slice(0, 2)],
      uncoveredSearch:
        regMap['uncovered'.slice(0, 3)] ?? regMap['uncovered'.slice(0, 2)] ?? '',
    });
    .as-console-wrapper { min-height: 100%!important; top: 0; }

    But in case the OP just wants to map over an existing simple structure like the provided one with only two known/fixated properties, the above generic solution can be boiled down into a much more efficient ("less resource intensive" with the OP's words) mapper implementation ...

    function createSanitizedItemFromBoundRegMap({ sku, stock }) {
      const regMap = this;
      return {
        sku: sku.replace(
          regMap[sku.slice(0, 3)] ??
          regMap[sku.slice(0, 2)] ??
          '', ''
        ),
        stock
      };
    }
    
    const sampleArray = [{
      sku: 'BD5000-50', stock: '0',
    }, {
      sku: 'PD-123-A S/A', stock: '0',
    }, {
      sku: 'KL.OTHERSTUFF', stock: '0',
    }, {
      sku: 'KL.EVE.R.STUFF', stock: '1',
    }, {
      sku: 'PD-123-B S/A', stock: '1',
    }, {
      sku: 'PD-123-C S/A', stock: '1',
    }, {
      sku: 'BD300-5000-5000', stock: '1',
    }];
    
    const resultArray = sampleArray
      .map(createSanitizedItemFromBoundRegMap, {
        'PD-': /\s*S\/A\s*/g,
        KL: /\.+/g,
        BD: /-50/g,
      });
    
    console.log({
      resultArray,
      sampleArray,
    });
    
    const firstArr = [{
      sku: 'PD-123-A S/A', stock: '0'
    }, {
      sku: 'PD-123-B S/A', stock: '1'
    }, {
      sku: 'PD-123-C S/A', stock: '2'
    }, {
      sku: 'PD-123-D', stock: '3'
    }, {
      sku: 'BD111-50', stock: '0'
    }, {
      sku: 'BD222-50', stock: '1'
    }, {
      sku: 'BD333-50', stock: '2'
    }, {
      sku: 'BD444', stock: '3'
    }, {
      sku: 'KL.111', stock: '0'
    }, {
      sku: 'KL.222', stock: '1'
    }, {
      sku: 'KL.333', stock: '2'
    }, {
      sku: 'KL444', stock: '3'
    }];
    
    console.log({
      resultArray: firstArr
        .map(createSanitizedItemFromBoundRegMap, {
          'PD-': /\s*S\/A\s*/g,
          KL: /\.+/g,
          BD: /-50/g,
        }),
      firstArr,
    });
    .as-console-wrapper { min-height: 100%!important; top: 0; }