How to filter UK postcodes - javascript

I'm trying to match the first part of a UK postcode to those that I have held in a JSON file. I'm doing this in Vue.
At the moment I have managed to match the postcode if it has 2 letters that match, but some UK postcodes do not start with 2 letters, some just have the one and this is where it fails.
See here for full code
https://codesandbox.io/s/48ywww0zk4
Sample of JSON
{
"id": 1,
"postcode": "AL",
"name": "St. Albans",
"zone": 3
},
{
"id": 2,
"postcode": "B",
"name": "Birmingham",
"zone": 2
},
{
"id": 3,
"postcode": "BA",
"name": "Bath",
"zone": 5
}
let postcodeZones = this.postcodeDetails.filter(
pc => pc.postcode
.toLowerCase()
.slice(0, 2)
.indexOf(this.selectPostcode.toLowerCase().slice(0, 2)) > -1
);
Can anyone help me find (for example) 'B' if I type B94 5RD & 'BA' if I type BA33HT?

You can use a regular expression that matches the alphabetical letters at the start of a string.
function getLettersBeforeNumbers( postcode ) {
return postcode.match( /^[a-zA-Z]*/ )[0];
}
let a = getLettersBeforeNumbers( 'B94 5RD' );
let b = getLettersBeforeNumbers( 'bA33HT' );
let c = getLettersBeforeNumbers( '33bA33HT' );
console.log( a, b, c );
/** EXPLANATION OF THE REGEXP
/ ^[a-zA-Z]* /
^ = anchor that signifies the start of the string
[ ... ] = select characters that are equal to ...
a-z = all characters in the alphabet
A-Z = all capatilised characters in the alphabet
* = zero or more occurances
**/
PS: You can just use the .match( /^[a-zA-Z]*/ )[0]; on your string.

Related

How to fix invalid JSON with RegExp in Javascript?

This is what I've tried
// input
let input = "{id: 1, name: apple, qty: 2, colors: [{id: 1, hex: #f95}], store: {id: 1, name: Apple Store}}"
let result = input.replace((/([\w]+)(:)/g), "\"$1\"$2");
// {"id": 1, "name": apple, "qty": 2, "colors": [{"id": 1, "hex": #f95}], "store": {"id": 1, "name": Apple Store}}
And then I just replace it like, replaceAll(': ', ': "'). I think it's not good practice to resolve it, may there is someone who can help me with this problem, thank you so much.
You can convert the stated string that looks almost like an object into an actual JavaScript object with the following assumptions:
keys are composed of alphanumeric and underscores chars
values are treated as numbers if they have the format of a number, e.g. an optional minus sign, followed by digits with optional .
values are treated as a string unless it has the form of a number, or start with [ (array) or { (object)
string values may not contain , or }
const input = "{id: 1, name: apple, qty: 2, colors: [{id: 1, hex: #f95}], store: {id: 1, name: Apple Store}}";
const regex1 = /([,\{] *)(\w+):/g;
const regex2 = /([,\{] *"\w+":)(?! *-?[0-9\.]+[,\}])(?! *[\{\[])( *)([^,\}]*)/g;
let json = input
.replace(regex1, '$1"$2":')
.replace(regex2, '$1$2"$3"')
let result = JSON.parse(json);
console.log(JSON.stringify(result, null, ' '));
Output:
{
"id": 1,
"name": "apple",
"qty": 2,
"colors": [
{
"id": 1,
"hex": "#f95"
}
],
"store": {
"id": 1,
"name": "Apple Store"
}
}
Explanation of regex1:
([,\{] *) -- capture group 1: , or {, followed by optional spaces
(\w+) -- capture group 2: 1+ word chars (alphanumeric and underscore)
: -- literal :
replace '$1"$2":' -- capture group 1, followed by capture group 2 enclosed in quotes, followed by colon
Explanation of regex2:
([,\{] *"\w+":) -- capture group 1: , or {, followed by optional spaces, quote, 1+ word chars, quote, colon
(?! *-?[0-9\.]+[,\}]) -- negative lookahead for optional spaces, a number, followed by , or }
(?! *[\{\[]) -- negative lookahead for optional spaces, followed by { or [
( *) -- capture group 2: optional spaces
([^,\}]*) -- capture group 3: everything that is not a , or }
replace '$1$2"$3"' -- capture group 1, followed by capture group 2, followed by capture group 3 enclosed in quotes
Learn more about regex: https://twiki.org/cgi-bin/view/Codev/TWikiPresentation2018x10x14Regex
Thanks for all answers, I tried this way and its works
class FixJson {
constructor() {
this.run = (json) => {
const fixDataType = (json) => {
for (const key in json) {
if (json.hasOwnProperty(key)) {
const value = json[key];
if (typeof value === 'object') {
fixDataType(value);
} else if (value === 'true' || value === 'false') {
json[key] = value === 'true';
} else if (!isNaN(value)) {
json[key] = Number(value);
}
}
}
return json;
}
// use the replace function to add double quotes around the property names
const fixedJson = json.replace(/([a-zA-Z0-9!##\$%\^\&*\)\(+=._-]+)/g, '"$1"');
// use the JSON.parse function to parse the fixed JSON string into a JavaScript object
const obj = JSON.parse(fixedJson.replaceAll('" "', ' '));
// fix json data type, and return the result
return fixDataType(obj)
}
}
}
const fix = new FixJson()
let result = fix.run("<your_invalid_json>")

Regex for substring on NodeJS using pinch.js

I am modifying JSON files/Javascript object using Pinch library: https://github.com/Baggz/Pinch
In this example, pinch() with regex parameter /id/ modifies ALL the id values to 321.
What I want to do is be able to change the value of all ids but only for a specific "Requestor", based on a parameter (requestorToChange). Let's say "RequestorX". How do I write the regex for it?
var sample = {
"RequestorX":
[{
user: {
id: '123'
},
request: {
id: '456'
},
book: {
id: '789'
}
}],
"RequestorY":
[{
user: {
id: '111'
},
request: {
id: '222'
},
book: {
id: '333'
}
}]
};
const requestorToChange = 'RequestorX'
pinch(sample, /id/, function(path, key, value) {
return '321';
});
console.log(JSON.stringify(sample))
I know that one option is to just do:
pinch(sample['RequestorX'], /id/, function(path, key, value) {
return '321';
});
But I need to be able to do it via the regex field since in reality, I will be manipulating deeply nested JSON files.
The explanation on the GitHub page is a bit sparse, but basically you start with the dot notation replacement style and replace the variable parts with regex patterns whilst adhering to the rules of JS regular expressions.
So, in your case, a dot notation pattern 'RequestorX[0].user.id'
becomes a regex like /RequestorX\[0\]\.(user|request|book)\.id/
Somes notes:
first we have to escape the square brackets because those have special meaning in regex
next, we also have to escape the . to use it as dot notation symbol
we use a group with alternations to replace the id on all of them
I tried to use new RegExp instead of the /../ notation to create a regex Object to add your constant to the pattern, unfortunately only the first item is replaced in this case, probably a bug...
pinch(sample, new RegExp(requestorToChange +'\[0\]\.(user|request|book)\.id'), '321');
Sample code (the SO code snippet outputs some extra gibberish about RequestorY, ignore it)
var sample = {
"RequestorX": [
{
"user": {
"id": "123"
},
"request": {
"id": "456"
},
"book": {
"id": "789"
}
}
],
"RequestorY": [
{
"user": {
"id": "123"
},
"request": {
"id": "456"
},
"book": {
"id": "789"
}
}
]
}
const requestorToChange = 'RequestorX';
//var result = pinch(sample, 'RequestorX[0].user.id', '321'); //replace the user id of RequestorX with dot notation
var result = pinch(sample, /RequestorX\[0\]\.(user|request|book)\.id/, '321');
//var result = pinch(sample, new RegExp(requestorToChange +'\[0\]\.(user|request|book)\.id'), '321');
console.log(JSON.stringify(result));
<script src="https://cdn.jsdelivr.net/npm/pinch#0.1.3/src/pinch.js"></script>
Alas, JSON + regex sounds a bit weird but the tool looks legit.

Peg.js distinguish between missing values and white space

I have the following peg.js script:
start = name*
name = '** name ' var ws 'var:' vr:var ws 'len:' n:num? ws 'label:' lb:label? 'type:' ws t:type? '**\n'
{return {NAME: vr,
LENGTH: n,
LABEL:lb,
TYPE: t
}}
type = 'CHAR'/'NUM'
var = $([a-zA-Z_][a-zA-Z0-9_]*)
label = p:labChar* { return p.join('')}
labChar = [^'"<>|\*\/]
ws = [\\t\\r ]
num = n:[0-9]+ {return n.join('')}
to parse:
** name a1 var:a1 len:9 label:The is the label for a1 type:NUM **
** name a2 var:a2 len: label:The is the label for a2 type:CHAR **
** name a3 var:a3 len:67 label: type: **
and I'm encountering a couple of issues.
Firstly, within the text that I am parsing I expect certain value labels such as 'var:', 'len:', 'label:' & 'type:'. I would like to use these labels, as I know they're fixed, to delineate between the values.
Secondly, I need to allow for missing values.
Am I going about this the correct way? Currently my script merges the value of the label with the type and then I get an error at :
Line 1, column 64: Expected "type:" or [^'"<>|*/] but "*" found.
Also, Can I do this with blocks of text too? I tried parsing :
** name a1 var:a1 len:9 label:The is the label for a1 type:NUM **
** name a2 var:a2 len: label:The is the label for a2 type:CHAR **
randomly created text ()= that I would like to keep
** name b1 var:b1 len:9 label:This is the label for b1 type:NUM **
** name b2 var:b2 len: label:This is the label for b2 type:CHAR **
more text
by amending the first line an adding the following:
start = (name/random)*
random = r:.+ (!'** name')
{return {RANDOM: r.join('')}}
I'm after a final result of:
[
[{
"NAME": "a1",
"LENGTH": "9",
"LABEL": "The is the label for a1",
"TYPE": "NUM"
},
{
"NAME": "a2",
"LENGTH": null,
"LABEL": "The is the label for a2",
"TYPE": "CHAR"
},
{"RANDOM":"randomly created text ()= that I would like to keep"}]
[{
"NAME": "b1",
"LENGTH": "9",
"LABEL": "This is the label for b1",
"TYPE": "NUM"
},
{
"NAME": "b2",
"LENGTH": null,
"LABEL": "This is the label for b2",
"TYPE": "CHAR"
},
{"RANDOM":"more text "}]
]
You'll want a negative lookahead !(ws 'type:') otherwise, the label rule will be too greedy and consume all the input to the end of the line.
As a side note, you can use the $() syntax to join the text of elements instead of {return n.join('')}.
start = name*
name = '** name ' var ws 'var:' vr:var ws 'len:' n:num? ws 'label:' lb:label? ws 'type:' t:type? ws '**' '\n'?
{return {NAME: vr,
LENGTH: n,
LABEL:lb,
TYPE: t
}}
var = $([a-zA-Z_][a-zA-Z0-9_]*)
num = $([0-9]+)
label = $((!(ws 'type:') [^'"<>|\*\/])*)
type = 'CHAR'/'NUM'
ws = [\\t\\r ]
Output:
[
{
"NAME": "a1",
"LENGTH": "9",
"LABEL": "The is the label for a1",
"TYPE": "NUM"
},
{
"NAME": "a2",
"LENGTH": null,
"LABEL": "The is the label for a2",
"TYPE": "CHAR"
},
{
"NAME": "a3",
"LENGTH": "67",
"LABEL": "",
"TYPE": null
}
]
Finally got the following to work:
random = r: $(!('** name').)+ {return {"RANDOM": r}}
I'm not sure I completely understand the syntax, but it works.

Javascript data structure for constant string mapping?

What data type(s) would provide easy lookup for a finite mapping of a pair of strings to one string
S X S |-> S ?
I wish to represent the mapping this way rather than as a function in order to make it easy to extend with simple rules.
Do I need to use arrays, e.g.
[ [[S1, S2], S3 ], [[S1, S3], S4 ], ...]
to represent the tuples? That seems to make lookups horrid. . . but I know that in ES6 keys can't be objects, so I see no other way..
A nested map (simple search tree) provides a generic solution:
const map = {
"one": {
"one": "11",
"two": "12"
},
"two": {
"one": "21",
"two": "22"
}
};
console.log(map["one"]["one"]); // "11"
console.log(map["one"]["two"]); // "12"
console.log(map["two"]["one"]); // "21"
console.log(map["two"]["two"]); // "22"
Alternatively, you can also concatenate two strings into a single key. But you need to prevent key collisions such as "a" + "ab" and "aa" + "b", e.g. by prefixing the key with the length of the first string:
const map = {
"3:oneone": "11",
"3:onetwo": "12",
"3:twoone": "21",
"3:twotwo": "22"
}
function set(map, str1, str2, val) {
return map[str1.length + ":" + str1 + str2] = val;
}
function get(map, str1, str2) {
return map[str1.length + ":" + str1 + str2];
}
console.log(get(map, "one", "one")); // "11"
console.log(get(map, "one", "two")); // "12"
console.log(get(map, "two", "one")); // "21"
console.log(get(map, "two", "two")); // "22"
If you do not like prefixing with the string length, you could e.g. use a separator "a" + ":" + "ab" but would then need to escape the separator character within the two strings. Personally, I prefer the nested map as demonstrated above for its genericity and simplicity.

How to trim the last one or last two characters of a string

I have an object with a bunch of strings:
[
{
date: "03/12/2014",
name: "mr blue",
title: "math teacher -"
},
{
date: "04/02/2015",
name: "mrs yellow",
title: "chemistry teacher"
},
{
date: "11/04/2014",
name: "mrs green",
title: "chemistry teacher - "
},
]
How can i strip the - from the title field if that string contains a -.
I know a can perform a slice/subtring:
var myvalue = myobject.title.substring(0, myobject.title.length-1);
However this will apply for all cases, and not just the ones that contain the -
Use replace:
var myvalue = myobject.title.replace(/\s*-\s*$/,'');
Bonus: with this regular expression only a dash at the end will be removed (along with the spaces around).
var title = 'math teacher -';
title = title.replace('-', '').trim();
document.write(title);
Update
Above will fail if title has dashes in the middle. Therefore, using lastIndexOf you can do
title = title.substring(0,oldString.lastIndexOf("-")).trim;

Categories