Skip to content

Commit be7a1d8

Browse files
committed
Added support for areas, sectors & units
Based on: http://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom#Formatting
1 parent bd82337 commit be7a1d8

6 files changed

Lines changed: 325 additions & 4 deletions

File tree

index.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
var validationRegex = /^[a-z0-9]{1,4}\s*?\d[a-z]{2}$/i,
44
incodeRegex = /\d[a-z]{2}$/i,
5-
validOutcodeRegex = /^[a-z0-9]{1,4}$/i;
5+
validOutcodeRegex = /^[a-z0-9]{1,4}$/i,
6+
areaRegex = /^[a-z]{1,2}/i,
7+
sectorRegex = /^[a-z0-9]{1,4}\s*?\d/i,
8+
unitRegex = /[a-z]{2}$/i;
69

710
function isValidPostcode (postcode) {
811
return !!postcode.match(validationRegex);
@@ -16,6 +19,18 @@ function parseIncode (postcode) {
1619
return postcode.match(incodeRegex)[0];
1720
}
1821

22+
function parseArea (postcode) {
23+
return postcode.match(areaRegex)[0];
24+
}
25+
26+
function parseSector (postcode) {
27+
return postcode.match(sectorRegex)[0];
28+
}
29+
30+
function parseUnit (postcode) {
31+
return postcode.match(unitRegex)[0];
32+
}
33+
1934
function Postcode (rawPostcode) {
2035
this._raw = rawPostcode;
2136
this._valid = isValidPostcode(rawPostcode);
@@ -47,10 +62,31 @@ Postcode.prototype.outcode = function () {
4762
return this._outcode;
4863
}
4964

65+
Postcode.prototype.area = function () {
66+
if (!this._valid) return null;
67+
if (this._area) return this._area;
68+
this._area = parseArea(this._raw).toUpperCase();
69+
return this._area;
70+
}
71+
72+
Postcode.prototype.sector = function () {
73+
if (!this._valid) return null;
74+
if (this._sector) return this._sector;
75+
this._sector = parseSector(this.normalise()).toUpperCase();
76+
return this._sector;
77+
}
78+
79+
Postcode.prototype.unit = function () {
80+
if (!this._valid) return null;
81+
if (this._unit) return this._unit;
82+
this._unit = parseUnit(this._raw).toUpperCase();
83+
return this._unit;
84+
}
85+
5086
Postcode.prototype.normalise = function () {
5187
if (!this._valid) return null;
5288
if (this.postcode) return this.postcode;
5389
return [this.outcode()," ", this.incode()].join("");
5490
}
5591

56-
module.exports = Postcode;
92+
module.exports = Postcode;

tests/data/areas.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"tests" : [
3+
{
4+
"base" : "L27 8XY",
5+
"expected" : "L"
6+
},
7+
{
8+
"base" : "NR10 3EZ",
9+
"expected" : "NR"
10+
},
11+
{
12+
"base" : "RG4 5AY",
13+
"expected" : "RG"
14+
},
15+
{
16+
"base" : "NE69 7AW",
17+
"expected" : "NE"
18+
},
19+
{
20+
"base" : "SE23 2NF",
21+
"expected" : "SE"
22+
},
23+
{
24+
"base" : "BT35 8GE",
25+
"expected" : "BT"
26+
},
27+
{
28+
"base" : "L278XY",
29+
"expected" : "L"
30+
},
31+
{
32+
"base" : "NR103EZ",
33+
"expected" : "NR"
34+
},
35+
{
36+
"base" : "RG45AY",
37+
"expected" : "RG"
38+
},
39+
{
40+
"base" : "NE697AW",
41+
"expected" : "NE"
42+
},
43+
{
44+
"base" : "SE232NF",
45+
"expected" : "SE"
46+
},
47+
{
48+
"base" : "BT358GE",
49+
"expected" : "BT"
50+
}
51+
]
52+
}

tests/data/sectors.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"tests" : [
3+
{
4+
"base" : "L27 8XY",
5+
"expected" : "L27 8"
6+
},
7+
{
8+
"base" : "NR10 3EZ",
9+
"expected" : "NR10 3"
10+
},
11+
{
12+
"base" : "RG4 5AY",
13+
"expected" : "RG4 5"
14+
},
15+
{
16+
"base" : "NE69 7AW",
17+
"expected" : "NE69 7"
18+
},
19+
{
20+
"base" : "SE23 2NF",
21+
"expected" : "SE23 2"
22+
},
23+
{
24+
"base" : "BT35 8GE",
25+
"expected" : "BT35 8"
26+
},
27+
{
28+
"base" : "L278XY",
29+
"expected" : "L27 8"
30+
},
31+
{
32+
"base" : "NR103EZ",
33+
"expected" : "NR10 3"
34+
},
35+
{
36+
"base" : "RG45AY",
37+
"expected" : "RG4 5"
38+
},
39+
{
40+
"base" : "NE697AW",
41+
"expected" : "NE69 7"
42+
},
43+
{
44+
"base" : "SE232NF",
45+
"expected" : "SE23 2"
46+
},
47+
{
48+
"base" : "BT358GE",
49+
"expected" : "BT35 8"
50+
}
51+
]
52+
}

tests/data/units.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"tests" : [
3+
{
4+
"base" : "L27 8XY",
5+
"expected" : "XY"
6+
},
7+
{
8+
"base" : "NR10 3EZ",
9+
"expected" : "EZ"
10+
},
11+
{
12+
"base" : "RG4 5AY",
13+
"expected" : "AY"
14+
},
15+
{
16+
"base" : "NE69 7AW",
17+
"expected" : "AW"
18+
},
19+
{
20+
"base" : "SE23 2NF",
21+
"expected" : "NF"
22+
},
23+
{
24+
"base" : "BT35 8GE",
25+
"expected" : "GE"
26+
},
27+
{
28+
"base" : "L278XY",
29+
"expected" : "XY"
30+
},
31+
{
32+
"base" : "NR103EZ",
33+
"expected" : "EZ"
34+
},
35+
{
36+
"base" : "RG45AY",
37+
"expected" : "AY"
38+
},
39+
{
40+
"base" : "NE697AW",
41+
"expected" : "AW"
42+
},
43+
{
44+
"base" : "SE232NF",
45+
"expected" : "NF"
46+
},
47+
{
48+
"base" : "BT358GE",
49+
"expected" : "GE"
50+
}
51+
]
52+
}

tests/exhaustive_unit.js

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,76 @@ describe("Exhaustive postcode test", function () {
111111
done();
112112
});
113113
});
114-
});
114+
describe("Area parsing", function () {
115+
it("should return the correct area", function (done) {
116+
this.timeout(60000);
117+
testData.forEach(function (testPostcode) {
118+
var pc = testPostcode[0],
119+
postcode = new Postcode(pc),
120+
downcasePostcode = new Postcode(pc.toLowerCase()),
121+
unspacedPostcode = new Postcode(pc.replace(/\s/, "")),
122+
testArea;
123+
if (pc.length === 7) {
124+
// Since this isn't normalised in dataset, best we can do is see if normalised data matches
125+
assert.equal(postcode.area(), downcasePostcode.area());
126+
assert.equal(postcode.area(), unspacedPostcode.area());
127+
} else {
128+
// Any space indicates incode/outcode
129+
testArea = pc.match(/\s.*/)[0].replace(/\s/, "");
130+
assert.equal(postcode.area(), testArea);
131+
assert.equal(downcasePostcode.area(), testArea);
132+
assert.equal(unspacedPostcode.area(), testArea);
133+
}
134+
});
135+
done();
136+
});
137+
});
138+
describe("Sector parsing", function () {
139+
it("should return the correct sector", function (done) {
140+
this.timeout(60000);
141+
testData.forEach(function (testPostcode) {
142+
var pc = testPostcode[0],
143+
postcode = new Postcode(pc),
144+
downcasePostcode = new Postcode(pc.toLowerCase()),
145+
unspacedPostcode = new Postcode(pc.replace(/\s/, "")),
146+
testSector;
147+
if (pc.length === 7) {
148+
// Since this isn't normalised in dataset, best we can do is see if normalised data matches
149+
assert.equal(postcode.sector(), downcasePostcode.sector());
150+
assert.equal(postcode.sector(), unspacedPostcode.sector());
151+
} else {
152+
// Any space indicates incode/outcode
153+
testSector = pc.match(/\s.*/)[0].replace(/\s/, "");
154+
assert.equal(postcode.sector(), testSector);
155+
assert.equal(downcasePostcode.sector(), testSector);
156+
assert.equal(unspacedPostcode.sector(), testSector);
157+
}
158+
});
159+
done();
160+
});
161+
});
162+
describe("Unit parsing", function () {
163+
it("should return the correct unit", function (done) {
164+
this.timeout(60000);
165+
testData.forEach(function (testPostcode) {
166+
var pc = testPostcode[0],
167+
postcode = new Postcode(pc),
168+
downcasePostcode = new Postcode(pc.toLowerCase()),
169+
unspacedPostcode = new Postcode(pc.replace(/\s/, "")),
170+
testUnit;
171+
if (pc.length === 7) {
172+
// Since this isn't normalised in dataset, best we can do is see if normalised data matches
173+
assert.equal(postcode.unit(), downcasePostcode.unit());
174+
assert.equal(postcode.unit(), unspacedPostcode.unit());
175+
} else {
176+
// Any space indicates incode/outcode
177+
testUnit = pc.match(/\s.*/)[0].replace(/\s/, "");
178+
assert.equal(postcode.unit(), testUnit);
179+
assert.equal(downcasePostcode.unit(), testUnit);
180+
assert.equal(unspacedPostcode.unit(), testUnit);
181+
}
182+
});
183+
done();
184+
});
185+
});
186+
});

tests/unit.js

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,61 @@ describe("Outcode parsing", function () {
9898
it ("should return null if invalid postcode", function () {
9999
assert.isNull(new Postcode("Definitly bogus").outcode());
100100
});
101-
});
101+
});
102+
103+
describe("Area parsing", function () {
104+
before(function (done) {
105+
testData = fs.readFile(path.join(dataDir, "areas.json"), function (error, data) {
106+
if (error) throw error;
107+
testData = JSON.parse(data);
108+
done();
109+
});
110+
});
111+
112+
it ("should correctly parse areas", function () {
113+
testData.tests.forEach(function (elem) {
114+
assert.equal(new Postcode(elem.base).area(), elem.expected);
115+
});
116+
});
117+
it ("should return null if invalid postcode", function () {
118+
assert.isNull(new Postcode("Definitely bogus").area());
119+
});
120+
});
121+
122+
describe("Sector parsing", function () {
123+
before(function (done) {
124+
testData = fs.readFile(path.join(dataDir, "sectors.json"), function (error, data) {
125+
if (error) throw error;
126+
testData = JSON.parse(data);
127+
done();
128+
});
129+
});
130+
131+
it ("should correctly parse sectors", function () {
132+
testData.tests.forEach(function (elem) {
133+
assert.equal(new Postcode(elem.base).sector(), elem.expected);
134+
});
135+
});
136+
it ("should return null if invalid postcode", function () {
137+
assert.isNull(new Postcode("Definitely bogus").sector());
138+
});
139+
});
140+
141+
describe("Unit parsing", function () {
142+
before(function (done) {
143+
testData = fs.readFile(path.join(dataDir, "units.json"), function (error, data) {
144+
if (error) throw error;
145+
testData = JSON.parse(data);
146+
done();
147+
});
148+
});
149+
150+
it ("should correctly parse units", function () {
151+
testData.tests.forEach(function (elem) {
152+
assert.equal(new Postcode(elem.base).unit(), elem.expected);
153+
});
154+
});
155+
it ("should return null if invalid postcode", function () {
156+
assert.isNull(new Postcode("Definitely bogus").unit());
157+
});
158+
});

0 commit comments

Comments
 (0)