The Variable Expansion Algorithm Using Regular Expression in Jav

  • 时间:2020-09-21 09:15:21
  • 分类:网络文摘
  • 阅读:143 次
JS The Variable Expansion Algorithm Using Regular Expression in Javascript javascript regex string

NodeJs / Javascript

Let’s say, if we want to expand strings that contain dynamic variables, we can use regular expression to do this easily.

1
2
3
4
5
const varContent = {
  VAR: "World",
}
 
console.log(expand("Hello, ${VAR}!", varContent)); // this is expected to print "Hello, World!"
const varContent = {
  VAR: "World",
}

console.log(expand("Hello, ${VAR}!", varContent)); // this is expected to print "Hello, World!"

Let’s first define an elegant class that will expose only 1 API – e.g. expand().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'use strict';
 
class VarExpansion {
  construct(varContent) {
    this.varContent = varContent;
  }
 
  expand(s) {
    if (!s) return s;
    const reg = /\$\{(\w+)\}/g;
    return s.replace(reg, (matched) => {
      const varName = matched.substring(2, matched.length - 1);
      // expand to empty if varName not existent
      // alternatively, we can leave the ${} untouched - replace '' with matched.
      return this.varContent [varName] !== undefined ? this.varContent [varName] : '';
    });    
  }
};
 
module.exports = VarExpansion;
'use strict';

class VarExpansion {
  construct(varContent) {
    this.varContent = varContent;
  }

  expand(s) {
    if (!s) return s;
    const reg = /\$\{(\w+)\}/g;
    return s.replace(reg, (matched) => {
      const varName = matched.substring(2, matched.length - 1);
      // expand to empty if varName not existent
      // alternatively, we can leave the ${} untouched - replace '' with matched.
      return this.varContent [varName] !== undefined ? this.varContent [varName] : '';
    });    
  }
};

module.exports = VarExpansion;

The easiest way to match a regex pattern and replace multiple instances of substrings is to use the String.prototype.replace() method, which takes the first parameter as a regex pattern, then we can specify the call back function for the matched substring.

The constructor of the class requires an object that contains the list of the dynamic contents that can be substituted on the fly. You can pass the Environmental Object (to substitute the sub string with environment variables in Node.JS environment) i.e. process.env or simply a mocked object if you want to unit test the variables expansion algorithm:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
'use strict';
 
const varExpansion = require('VarExpansion');
const {assert} = require('chai');
 
describe('Unit Tests for Variable Expansion in String', () => {
  const mockVarObject = {
    data: 'World',
  };
 
  let varExpansion;
  beforeEach(() => {
    varExpansion = new VarExpansion(mockVarObject);
  });
 
  describe('expansion tests', () => {
    it('should expand', () => {
      // eslint-disable-next-line no-template-curly-in-string
      const s = 'Hello, ${data}!';
      const t = varExpansion.expand(s);
      assert.equal(t, 'Hello, World!');
    });
  }
}
'use strict';

const varExpansion = require('VarExpansion');
const {assert} = require('chai');

describe('Unit Tests for Variable Expansion in String', () => {
  const mockVarObject = {
    data: 'World',
  };

  let varExpansion;
  beforeEach(() => {
    varExpansion = new VarExpansion(mockVarObject);
  });

  describe('expansion tests', () => {
    it('should expand', () => {
      // eslint-disable-next-line no-template-curly-in-string
      const s = 'Hello, ${data}!';
      const t = varExpansion.expand(s);
      assert.equal(t, 'Hello, World!');
    });
  }
}

Like the clone function in Javascript: How to Clone Variables (The Clone Function) in Javascript?, we can extend the expand() method that allows us to pass in the object or arrays – which will be expanded recursively (with deep copies)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
'use strict';
 
class VarExpansion {
  construct(varContent) {
    this.varContent = varContent;
  }
 
  expand(s) {
    if (!s) return s;
    if (s instanceof String) {
      const r = /\$\{(\w+)\}/g;
      return s.replace(r, (matched) => {
        const varName = matched.substring(2, matched.length - 1);
        // expand to empty if varName not existent
        // alternatively, we can leave the ${} untouched - replace '' with matched.
        return this.env[varName] !== undefined ? this.env[varName] : '';
      });
    }
    if (s instanceof Array) {
      const buf = [];  // a deep copy of array
      let i = s.length;
      while (i--) {
        buf[i] = this.expand(s[i]);
      }
      return buf;
    }
    if (s instanceof Object) {
      const buf = {};  // a deep copy of object
      for (const key in s) {
        if (s.hasOwnProperty(key)) { // filter out other keys belong to other arrays
          buf[key] = this.expand(s[key]); // clone and expand the key/value
        }
      }
      return buf;
    }
    return s;   
  }
};
 
module.exports = VarExpansion;
'use strict';

class VarExpansion {
  construct(varContent) {
    this.varContent = varContent;
  }

  expand(s) {
    if (!s) return s;
    if (s instanceof String) {
      const r = /\$\{(\w+)\}/g;
      return s.replace(r, (matched) => {
        const varName = matched.substring(2, matched.length - 1);
        // expand to empty if varName not existent
        // alternatively, we can leave the ${} untouched - replace '' with matched.
        return this.env[varName] !== undefined ? this.env[varName] : '';
      });
    }
    if (s instanceof Array) {
      const buf = [];  // a deep copy of array
      let i = s.length;
      while (i--) {
        buf[i] = this.expand(s[i]);
      }
      return buf;
    }
    if (s instanceof Object) {
      const buf = {};  // a deep copy of object
      for (const key in s) {
        if (s.hasOwnProperty(key)) { // filter out other keys belong to other arrays
          buf[key] = this.expand(s[key]); // clone and expand the key/value
        }
      }
      return buf;
    }
    return s;   
  }
};

module.exports = VarExpansion;

And this is very powerful as you can pass in JSON objects:

1
2
3
4
5
6
7
8
  describe('expansion tests', () => {
    it('should expand', () => {
      // eslint-disable-next-line no-template-curly-in-string
      const s = { Hello: '${data}!' };
      const t = varExpansion.expand(s);
      assert.deepEqual(t, { Hello: 'World!' });
    });
  }
  describe('expansion tests', () => {
    it('should expand', () => {
      // eslint-disable-next-line no-template-curly-in-string
      const s = { Hello: '${data}!' };
      const t = varExpansion.expand(s);
      assert.deepEqual(t, { Hello: 'World!' });
    });
  }

It turns out we can use the lodash npm library to do the string substitution via the template engine.

1
2
3
4
5
6
7
  expand(s) {
    try {
      return JSON.parse(_.template(JSON.stringify(s))(this.env));
    } catch (e) {
      return s;
    }
  }
  expand(s) {
    try {
      return JSON.parse(_.template(JSON.stringify(s))(this.env));
    } catch (e) {
      return s;
    }
  }

However, this has slightly different behaviors: this is case in-sensitive and also, when variables not found, it will not change the string substitution syntax.

–EOF (The Ultimate Computing & Technology Blog) —

推荐阅读:
CRI环球资讯广播FM90.5在线收听「听广播」  香港卫视直播-香港卫视在线直播观看「高清」  翡翠台直播-TVB翡翠台在线直播「高清」  凤凰卫视中文台在线直播「高清」  凤凰卫视资讯台直播-凤凰资讯台在线直播「高清」  星空卫视直播-星空卫视在线直播观看「高清」  健康卫视直播-健康卫视在线直播观看「高清」  莲花卫视直播-莲花卫视在线直播观看「高清」  澳亚卫视直播-澳亚卫视在线直播观看「高清」  阳光卫视直播-阳光卫视在线直播观看「高清」 
评论列表
添加评论