From 3ee25aa97746dd6f4f1fb63806f18a71679884c8 Mon Sep 17 00:00:00 2001 From: James Billingham Date: Fri, 28 Aug 2015 09:23:37 +0100 Subject: [PATCH 1/3] Added `district` and `subDistrict` as-per issue #3 --- index.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/index.js b/index.js index 5b7ce5e..922da9c 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ var validationRegex = /^[a-z0-9]{1,4}\s*?\d[a-z]{2}$/i, incodeRegex = /\d[a-z]{2}$/i, validOutcodeRegex = /^[a-z0-9]{1,4}$/i, areaRegex = /^[a-z]{1,2}/i, + districtSplitRegex = /^([a-z]{1,2}\d)([a-z])$/i, sectorRegex = /^[a-z0-9]{1,4}\s*?\d/i, unitRegex = /[a-z]{2}$/i; @@ -69,6 +70,24 @@ Postcode.prototype.area = function () { return this._area; } +Postcode.prototype.district = function () { + if (!this._valid) return null; + if (this._district) return this._district; + var outcode = this.outcode(); + var split = outcode.match(districtSplitRegex); + this._district = split ? split[1] : outcode; + return this._district; +} + +Postcode.prototype.subDistrict = function () { + if (!this._valid) return null; + if (this._subDistrict) return this._subDistrict; + var outcode = this.outcode(); + var split = outcode.match(districtSplitRegex); + this._subDistrict = split ? outcode : null; + return this._subDistrict; +} + Postcode.prototype.sector = function () { if (!this._valid) return null; if (this._sector) return this._sector; From d19d663d6ac274e30ef868061ef8c9f306a832d2 Mon Sep 17 00:00:00 2001 From: James Billingham Date: Fri, 28 Aug 2015 09:42:39 +0100 Subject: [PATCH 2/3] Added tests for `district` and `subDistrict` --- tests/data/districts.json | 68 +++++++++++++++++++++++++++++++++++ tests/data/sub-districts.json | 68 +++++++++++++++++++++++++++++++++++ tests/exhaustive_unit.js | 48 +++++++++++++++++++++++++ tests/unit.js | 38 ++++++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 tests/data/districts.json create mode 100644 tests/data/sub-districts.json diff --git a/tests/data/districts.json b/tests/data/districts.json new file mode 100644 index 0000000..28a1e92 --- /dev/null +++ b/tests/data/districts.json @@ -0,0 +1,68 @@ +{ + "tests" : [ + { + "base" : "L27 8XY", + "expected" : "L27" + }, + { + "base" : "NR10 3EZ", + "expected" : "NR10" + }, + { + "base" : "RG4 5AY", + "expected" : "RG4" + }, + { + "base" : "NE69 7AW", + "expected" : "NE69" + }, + { + "base" : "SE23 2NF", + "expected" : "SE23" + }, + { + "base" : "BT35 8GE", + "expected" : "BT35" + }, + { + "base" : "L278XY", + "expected" : "L27" + }, + { + "base" : "NR103EZ", + "expected" : "NR10" + }, + { + "base" : "RG45AY", + "expected" : "RG4" + }, + { + "base" : "NE697AW", + "expected" : "NE69" + }, + { + "base" : "SE232NF", + "expected" : "SE23" + }, + { + "base" : "BT358GE", + "expected" : "BT35" + }, + { + "base" : "EC1A 1BB", + "expected" : "EC1" + }, + { + "base" : "W1A0AX", + "expected" : "W1" + }, + { + "base" : "NW1W1AA", + "expected" : "NW1" + }, + { + "base" : "N1C 0AB", + "expected" : "N1" + } + ] +} diff --git a/tests/data/sub-districts.json b/tests/data/sub-districts.json new file mode 100644 index 0000000..8cafb7d --- /dev/null +++ b/tests/data/sub-districts.json @@ -0,0 +1,68 @@ +{ + "tests" : [ + { + "base" : "L27 8XY", + "expected" : null + }, + { + "base" : "NR10 3EZ", + "expected" : null + }, + { + "base" : "RG4 5AY", + "expected" : null + }, + { + "base" : "NE69 7AW", + "expected" : null + }, + { + "base" : "SE23 2NF", + "expected" : null + }, + { + "base" : "BT35 8GE", + "expected" : null + }, + { + "base" : "L278XY", + "expected" : null + }, + { + "base" : "NR103EZ", + "expected" : null + }, + { + "base" : "RG45AY", + "expected" : null + }, + { + "base" : "NE697AW", + "expected" : null + }, + { + "base" : "SE232NF", + "expected" : null + }, + { + "base" : "BT358GE", + "expected" : null + }, + { + "base" : "EC1A 1BB", + "expected" : "EC1A" + }, + { + "base" : "W1A0AX", + "expected" : "W1A" + }, + { + "base" : "NW1W1AA", + "expected" : "NW1W" + }, + { + "base" : "N1C 0AB", + "expected" : "N1C" + } + ] +} diff --git a/tests/exhaustive_unit.js b/tests/exhaustive_unit.js index 2e26542..318922a 100644 --- a/tests/exhaustive_unit.js +++ b/tests/exhaustive_unit.js @@ -135,6 +135,54 @@ describe("Exhaustive postcode test", function () { done(); }); }); + describe("District parsing", function () { + it("should return the correct district", function (done) { + this.timeout(60000); + testData.forEach(function (testPostcode) { + var pc = testPostcode[0], + postcode = new Postcode(pc), + downcasePostcode = new Postcode(pc.toLowerCase()), + unspacedPostcode = new Postcode(pc.replace(/\s/, "")), + testDistrict; + if (pc.length === 7) { + // Since this isn't normalised in dataset, best we can do is see if normalised data matches + assert.equal(postcode.district(), downcasePostcode.district()); + assert.equal(postcode.district(), unspacedPostcode.district()); + } else { + // Any space indicates incode/outcode + testDistrict = pc.match(/\s.*/)[0].replace(/\s/, ""); + assert.equal(postcode.district(), testDistrict); + assert.equal(downcasePostcode.district(), testDistrict); + assert.equal(unspacedPostcode.district(), testDistrict); + } + }); + done(); + }); + }); + describe("Sub-district parsing", function () { + it("should return the correct sub-district", function (done) { + this.timeout(60000); + testData.forEach(function (testPostcode) { + var pc = testPostcode[0], + postcode = new Postcode(pc), + downcasePostcode = new Postcode(pc.toLowerCase()), + unspacedPostcode = new Postcode(pc.replace(/\s/, "")), + testSubDistrict; + if (pc.length === 7) { + // Since this isn't normalised in dataset, best we can do is see if normalised data matches + assert.equal(postcode.subDistrict(), downcasePostcode.subDistrict()); + assert.equal(postcode.subDistrict(), unspacedPostcode.subDistrict()); + } else { + // Any space indicates incode/outcode + testSubDistrict = pc.match(/\s.*/)[0].replace(/\s/, ""); + assert.equal(postcode.subDistrict(), testSubDistrict); + assert.equal(downcasePostcode.subDistrict(), testSubDistrict); + assert.equal(unspacedPostcode.subDistrict(), testSubDistrict); + } + }); + done(); + }); + }); describe("Sector parsing", function () { it("should return the correct sector", function (done) { this.timeout(60000); diff --git a/tests/unit.js b/tests/unit.js index 6bfcd42..f8b6066 100644 --- a/tests/unit.js +++ b/tests/unit.js @@ -119,6 +119,44 @@ describe("Area parsing", function () { }); }); +describe("District parsing", function () { + before(function (done) { + testData = fs.readFile(path.join(dataDir, "districts.json"), function (error, data) { + if (error) throw error; + testData = JSON.parse(data); + done(); + }); + }); + + it ("should correctly parse districts", function () { + testData.tests.forEach(function (elem) { + assert.equal(new Postcode(elem.base).district(), elem.expected); + }); + }); + it ("should return null if invalid postcode", function () { + assert.isNull(new Postcode("Definitely bogus").district()); + }); +}); + +describe("Sub-district parsing", function () { + before(function (done) { + testData = fs.readFile(path.join(dataDir, "sub-districts.json"), function (error, data) { + if (error) throw error; + testData = JSON.parse(data); + done(); + }); + }); + + it ("should correctly parse sub-districts", function () { + testData.tests.forEach(function (elem) { + assert.equal(new Postcode(elem.base).subDistrict(), elem.expected); + }); + }); + it ("should return null if invalid postcode", function () { + assert.isNull(new Postcode("Definitely bogus").subDistrict()); + }); +}); + describe("Sector parsing", function () { before(function (done) { testData = fs.readFile(path.join(dataDir, "sectors.json"), function (error, data) { From 04da335b34abfbbf6c007ca9a882c08ae4f77904 Mon Sep 17 00:00:00 2001 From: James Billingham Date: Fri, 28 Aug 2015 10:04:31 +0100 Subject: [PATCH 3/3] Updated readme for `district` and `subDistrict` --- README.md | 63 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 5d31b98..a449226 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -[![Build Status](https://travis-ci.org/cblanc/postcode.js.png)](https://travis-ci.org/cblanc/postcode.js) +[![Build Status](https://travis-ci.org/cblanc/postcode.js.png)](https://travis-ci.org/cblanc/postcode.js) [![Dependency Status](https://gemnasium.com/cblanc/postcode.js.png)](https://gemnasium.com/cblanc/postcode.js) # Postcodes Utility methods for UK Postcodes. -Included is a test suite that tests against all postcodes listed in the Ordnance Survey's postcode dataset as of January 2014. +Included is a test suite that tests against all postcodes listed in the Ordnance Survey's postcode dataset as of January 2014. ## Getting Started @@ -16,25 +16,22 @@ Create an instance of Postcode to perform utility methods, like so ```javascript var Postcode = require("postcode"); -var postcode = new Postcode("po167gz"); +var postcode = new Postcode("ec1v9lb"); ``` Perform simple validations, parsing and normalisation ```javascript -postcode.valid() // => True - -postcode.normalise() // => "PO16 7GZ" - -postcode.outcode() // => "PO16" - -postcode.incode() // => "7GZ" - -postcode.area() // => "PO" - -postcode.sector() // => "PO16 7" - -postcode.unit() // => "GZ" +postcode.valid() // => true +postcode.normalise() // => "EC1V 9LB" + +postcode.outcode() // => "EC1V" +postcode.incode() // => "9LB" +postcode.area() // => "EC" +postcode.district() // => "EC1" +postcode.subDistrict() // => "EC1V" +postcode.sector() // => "EC1V 9" +postcode.unit() // => "LB" ``` Misc. Class Methods include @@ -45,27 +42,47 @@ Postcode.validOutcode(outcode) ## Definitions -### Outcode (#outcode) +### Outcode The outward code is the part of the postcode before the single space in the middle. It is between two and four characters long. A few outward codes are non-geographic, not divulging where mail is to be sent. Examples of outward codes include "L1", "W1A", "RH1", "RH10" or "SE1P". -### Inward Code (#inward) +### Incode The inward part is the part of the postcode after the single space in the middle. It is three characters long. The inward code assists in the delivery of post within a postal district. Examples of inward codes include "0NY", "7GZ", "7HF", or "8JQ". -### Postcode Area (#area) +### Area The postcode area is part of the outward code. The postcode area is between one and two characters long and is all letters. Examples of postcode areas include "L" for Liverpool, "RH" for Redhill and "EH" Edinburgh. A postal area may cover a wide area, for example "RH" covers north Sussex, (which has little to do with Redhill historically apart from the railway links), and "BT" (Belfast) covers the whole of Northern Ireland. -### Postcode Sector (#sector) +### District + +The district code is part of the outward code. It is between two and four characters long. It does not include the trailing letter found in some outcodes. Examples of district codes include "L1", "W1", "RH1", "RH10" or "SE1". + +### Sub-District + +The sub-district code is part of the outward code. It is often not present, only existing in particularly high density London districts. It is between three and four characters long. It does include the trailing letter omitted from the district. Examples of sub-district codes include "W1A", "EC1A", "NW1W", "E1W" or "SE1P". + +Note: for outcodes not ending with a letter, `subDistrict` will return `null`. For example: + +```js +new Postcode("SW1A 1AA").subDistrict() // => SW1A +new Postcode("E1W 1LD").subDistrict() // => E1W +new Postcode("PO16 7GZ").subDistrict() // => null +new Postcode("B5 5NY").subDistrict() // => null +``` + +### Sector The postcode sector is made up of the postcode district, the single space, and the first character of the inward code. It is between four and six characters long (including the single space). Examples of postcode sectors include "SW1W 0", "PO16 7", "GU16 7", or "L1 8", "CV1 4". -### Postcode Unit (#unit) +### Unit The postcode unit is two characters added to the end of the postcode sector. Each postcode unit generally represents a street, part of a street, a single address, a group of properties, a single property, a sub-section of the property, an individual organisation or (for instance Driver and Vehicle Licensing Agency) a subsection of the organisation. The level of discrimination is often based on the amount of mail received by the premises or business. Examples of postcode units include "SW1W 0NY", "PO16 7GZ", "GU16 7HF", or "L1 8JQ". -Source: [Wikipedia](http://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom#Formatting) +Sources: + +- https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom#Formatting +- https://en.wikipedia.org/wiki/London_postal_district#Numbered_divisions ## Testing @@ -81,4 +98,4 @@ A complete list of Postcodes can be obtained from the ONS Postcode Directory, wh MIT -Contains Ordnance Survey Data © Crown Copyright & Database Right 2014 \ No newline at end of file +Contains Ordnance Survey Data © Crown Copyright & Database Right 2014