Skip to content

Commit f981d8f

Browse files
committed
feat: add isUPC for Universal Product Codes (UPC-A)
1 parent d457eca commit f981d8f

4 files changed

Lines changed: 61 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ Validator | Description
106106
**isDecimal(str [, options])** | check if the string represents a decimal number, such as 0.1, .3, 1.1, 1.00003, 4.0, etc.<br/><br/>`options` is an object which defaults to `{force_decimal: false, decimal_digits: '1,', locale: 'en-US'}`.<br/><br/>`locale` determines the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'eo', 'es-ES', 'fa', 'fa-AF', 'fa-IR', 'fr-FR', 'fr-CA', 'hu-HU', 'id-ID', 'it-IT', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pl-Pl', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA', 'vi-VN']`.<br/>**Note:** `decimal_digits` is given as a range like '1,3', a specific value like '3' or min like '1,'.
107107
**isDivisibleBy(str, number)** | check if the string is a number that is divisible by another.
108108
**isEAN(str)** | check if the string is an [EAN (European Article Number)][European Article Number].
109+
**isUPC(str)** | check if the string is a [UPC-A][UPC-A] code with a valid checksum.
109110
**isEmail(str [, options])** | check if the string is an email.<br/><br/>`options` is an object which defaults to `{ allow_display_name: false, require_display_name: false, allow_utf8_local_part: true, require_tld: true, allow_ip_domain: false, allow_underscores: false, domain_specific_validation: false, blacklisted_chars: '', host_blacklist: [] }`. If `allow_display_name` is set to true, the validator will also match `Display Name <email-address>`. If `require_display_name` is set to true, the validator will reject strings without the format `Display Name <email-address>`. If `allow_utf8_local_part` is set to false, the validator will not allow any non-English UTF8 character in email address' local part. If `require_tld` is set to false, email addresses without a TLD in their domain will also be matched. If `ignore_max_length` is set to true, the validator will not check for the standard max length of an email. If `allow_ip_domain` is set to true, the validator will allow IP addresses in the host part. If `domain_specific_validation` is true, some additional validation will be enabled, e.g. disallowing certain syntactically valid email addresses that are rejected by Gmail. If `blacklisted_chars` receives a string, then the validator will reject emails that include any of the characters in the string, in the name part. If `host_blacklist` is set to an array of strings or regexp, and the part of the email after the `@` symbol matches one of the strings defined in it, the validation fails. If `host_whitelist` is set to an array of strings or regexp, and the part of the email after the `@` symbol matches none of the strings defined in it, the validation fails.
110111
**isEmpty(str [, options])** | check if the string has a length of zero.<br/><br/>`options` is an object which defaults to `{ ignore_whitespace: false }`.
111112
**isEthereumAddress(str)** | check if the string is an [Ethereum][Ethereum] address. Does not validate address checksums.
@@ -246,6 +247,7 @@ This project is licensed under the [MIT](LICENSE). See the [LICENSE](LICENSE) fi
246247
[Base64 URL Safe]: https://base64.guru/standards/base64url
247248
[Data URI Format]: https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs
248249
[European Article Number]: https://en.wikipedia.org/wiki/International_Article_Number
250+
[UPC-A]: https://en.wikipedia.org/wiki/Universal_Product_Code
249251
[Ethereum]: https://ethereum.org/
250252
[CSS Colors Level 4 Specification]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
251253
[IMEI]: https://en.wikipedia.org/wiki/International_Mobile_Equipment_Identity

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ import isCreditCard from './lib/isCreditCard';
7777
import isIdentityCard from './lib/isIdentityCard';
7878

7979
import isEAN from './lib/isEAN';
80+
import isUPC from './lib/isUPC';
8081
import isISIN from './lib/isISIN';
8182
import isISBN from './lib/isISBN';
8283
import isISSN from './lib/isISSN';
@@ -198,6 +199,7 @@ const validator = {
198199
isCreditCard,
199200
isIdentityCard,
200201
isEAN,
202+
isUPC,
201203
isISIN,
202204
isISBN,
203205
isISSN,

src/lib/isUPC.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import assertString from './util/assertString';
2+
3+
const UPCA_REGEX = /^\d{12}$/;
4+
5+
function calculateCheckDigit(upc) {
6+
const digits = upc
7+
.slice(0, -1)
8+
.split('')
9+
.map(Number);
10+
11+
const sumOddPositions = digits
12+
.filter((_, index) => index % 2 === 0)
13+
.reduce((acc, digit) => acc + digit, 0);
14+
15+
const sumEvenPositions = digits
16+
.filter((_, index) => index % 2 === 1)
17+
.reduce((acc, digit) => acc + digit, 0);
18+
19+
const total = (sumOddPositions * 3) + sumEvenPositions;
20+
const remainder = total % 10;
21+
22+
return remainder === 0 ? 0 : 10 - remainder;
23+
}
24+
25+
export default function isUPC(str) {
26+
assertString(str);
27+
28+
if (!UPCA_REGEX.test(str)) {
29+
return false;
30+
}
31+
32+
const actualCheckDigit = Number(str.slice(-1));
33+
34+
return actualCheckDigit === calculateCheckDigit(str);
35+
}
36+

test/validators.test.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7072,6 +7072,27 @@ describe('Validators', () => {
70727072
});
70737073
});
70747074

7075+
it('should validate UPCs', () => {
7076+
test({
7077+
validator: 'isUPC',
7078+
valid: [
7079+
'036000291452',
7080+
'012345678905',
7081+
'042100005264',
7082+
'725272730706',
7083+
'013800150738',
7084+
'015000000394',
7085+
],
7086+
invalid: [
7087+
'036000291453',
7088+
'725272730700',
7089+
'01234567890',
7090+
'0123456789057',
7091+
'12345678A905',
7092+
],
7093+
});
7094+
});
7095+
70757096
it('should validate ISSNs', () => {
70767097
test({
70777098
validator: 'isISSN',

0 commit comments

Comments
 (0)