or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

as-you-type-formatting.mdcore-parsing-formatting.mdindex.mdnumber-validation.mdphone-number-matching.mdregion-metadata.mdshort-numbers.mdutility-functions.md

utility-functions.mddocs/

0

# Utility Functions

1

2

Helper functions for normalizing, converting, and analyzing phone numbers. These utilities support data processing, string manipulation, and number comparison operations.

3

4

## Capabilities

5

6

### String Normalization

7

8

Normalize phone number strings by removing or converting unwanted characters to standard formats.

9

10

```python { .api }

11

def normalize_digits_only(number: str) -> str:

12

"""

13

Normalize string to contain only digits.

14

15

Removes all non-digit characters from the input string.

16

17

Parameters:

18

- number: String that may contain phone number with formatting

19

20

Returns:

21

String containing only digit characters (0-9)

22

"""

23

24

def normalize_diallable_chars_only(number: str) -> str:

25

"""

26

Normalize string to contain only diallable characters.

27

28

Keeps digits and diallable symbols like +, *, #, but removes

29

formatting characters like spaces, dashes, parentheses.

30

31

Parameters:

32

- number: Phone number string with potential formatting

33

34

Returns:

35

String with only diallable characters

36

"""

37

```

38

39

**Usage Examples:**

40

41

```python

42

import phonenumbers

43

44

# Remove all formatting

45

formatted_number = "+44 (20) 8366-1177"

46

digits_only = phonenumbers.normalize_digits_only(formatted_number)

47

print(digits_only) # "442083661177"

48

49

# Keep diallable characters

50

number_with_extension = "+44 20 8366 1177 ext. 123"

51

diallable = phonenumbers.normalize_diallable_chars_only(number_with_extension)

52

print(diallable) # "+442083661177ext123"

53

```

54

55

### Alpha Character Conversion

56

57

Convert alphabetic characters in phone numbers to their corresponding digits (vanity numbers).

58

59

```python { .api }

60

def convert_alpha_characters_in_number(number: str) -> str:

61

"""

62

Convert alphabetic characters to digits using phone keypad mapping.

63

64

Maps letters to digits according to standard phone keypad:

65

ABC=2, DEF=3, GHI=4, JKL=5, MNO=6, PQRS=7, TUV=8, WXYZ=9

66

67

Parameters:

68

- number: Phone number string that may contain letters

69

70

Returns:

71

String with letters converted to corresponding digits

72

"""

73

74

def is_alpha_number(number: str) -> bool:

75

"""

76

Check if a phone number string contains alphabetic characters.

77

78

Parameters:

79

- number: Phone number string to check

80

81

Returns:

82

True if the string contains 3 or more letters, False otherwise

83

"""

84

```

85

86

**Usage Examples:**

87

88

```python

89

import phonenumbers

90

91

# Convert vanity number

92

vanity_number = "1-800-FLOWERS"

93

numeric = phonenumbers.convert_alpha_characters_in_number(vanity_number)

94

print(numeric) # "1-800-3569377"

95

96

# Check for alpha characters

97

has_alpha = phonenumbers.is_alpha_number("1-800-CALL-NOW")

98

print(has_alpha) # True

99

100

no_alpha = phonenumbers.is_alpha_number("1-800-555-1234")

101

print(no_alpha) # False

102

```

103

104

### Number Comparison

105

106

Compare phone numbers to determine if they represent the same number and the quality of the match.

107

108

```python { .api }

109

def is_number_match(first_number: PhoneNumber, second_number: PhoneNumber) -> MatchType:

110

"""

111

Compare two phone numbers and return the type of match.

112

113

Parameters:

114

- first_number: First PhoneNumber object to compare

115

- second_number: Second PhoneNumber object to compare

116

117

Returns:

118

MatchType indicating the quality of the match:

119

- EXACT_MATCH: Numbers are identical in all respects

120

- NSN_MATCH: National significant numbers match exactly

121

- SHORT_NSN_MATCH: One NSN is a shorter version of the other

122

- NO_MATCH: Numbers are different

123

- NOT_A_NUMBER: One or both numbers are invalid

124

"""

125

```

126

127

**Usage Examples:**

128

129

```python

130

import phonenumbers

131

from phonenumbers import MatchType

132

133

# Parse two representations of the same number

134

number1 = phonenumbers.parse("+442083661177")

135

number2 = phonenumbers.parse("020 8366 1177", "GB")

136

137

match_result = phonenumbers.is_number_match(number1, number2)

138

139

if match_result == MatchType.EXACT_MATCH:

140

print("Numbers are exactly the same")

141

elif match_result == MatchType.NSN_MATCH:

142

print("Numbers have the same national significant number")

143

elif match_result == MatchType.SHORT_NSN_MATCH:

144

print("One number is a shorter version of the other")

145

elif match_result == MatchType.NO_MATCH:

146

print("Numbers are different")

147

148

# Compare with extensions

149

number_with_ext = phonenumbers.parse("+442083661177", keep_raw_input=True)

150

number_with_ext.extension = "123"

151

number_without_ext = phonenumbers.parse("+442083661177")

152

153

match_with_extension = phonenumbers.is_number_match(number_with_ext, number_without_ext)

154

print(f"Match with extension: {match_with_extension}")

155

```

156

157

### National Significant Number Extraction

158

159

Extract the national significant number portion from a parsed phone number.

160

161

```python { .api }

162

def national_significant_number(numobj: PhoneNumber) -> str:

163

"""

164

Get the national significant number from a PhoneNumber object.

165

166

The national significant number is the portion of the phone number

167

that is dialed within the country, excluding the country code but

168

including area codes and the subscriber number.

169

170

Parameters:

171

- numobj: PhoneNumber object to extract NSN from

172

173

Returns:

174

String containing the national significant number

175

"""

176

```

177

178

### Area Code and Destination Code Analysis

179

180

Analyze the structure of phone numbers to determine area code and destination code lengths.

181

182

```python { .api }

183

def length_of_geographical_area_code(numobj: PhoneNumber) -> int:

184

"""

185

Get the length of the geographical area code for a phone number.

186

187

Parameters:

188

- numobj: PhoneNumber object to analyze

189

190

Returns:

191

Length of the area code, or 0 if not applicable or determinable

192

"""

193

194

def length_of_national_destination_code(numobj: PhoneNumber) -> int:

195

"""

196

Get the length of the national destination code (area code + carrier code).

197

198

Parameters:

199

- numobj: PhoneNumber object to analyze

200

201

Returns:

202

Length of the national destination code

203

"""

204

```

205

206

### Mobile Token Handling

207

208

Work with mobile tokens used in certain countries before the area code.

209

210

```python { .api }

211

def country_mobile_token(country_calling_code: int) -> str:

212

"""

213

Get the mobile token for a country, if applicable.

214

215

Some countries use a mobile token (like '9' in Argentina) that

216

appears before the area code in mobile numbers.

217

218

Parameters:

219

- country_calling_code: Country calling code to check

220

221

Returns:

222

Mobile token string, or empty string if not applicable

223

"""

224

```

225

226

### NANPA Region Detection

227

228

Check if a region uses the North American Numbering Plan.

229

230

```python { .api }

231

def is_nanpa_country(region_code: str) -> bool:

232

"""

233

Check if a region uses the North American Numbering Plan.

234

235

NANPA regions share country code +1 and similar numbering patterns.

236

237

Parameters:

238

- region_code: Two-letter region code to check

239

240

Returns:

241

True if the region is part of NANPA, False otherwise

242

"""

243

```

244

245

### Mobile Number Portability

246

247

Check if a region supports mobile number portability.

248

249

```python { .api }

250

def is_mobile_number_portable_region(region_code: str) -> bool:

251

"""

252

Check if mobile numbers can be ported between carriers in a region.

253

254

Parameters:

255

- region_code: Two-letter region code to check

256

257

Returns:

258

True if mobile number portability is supported, False otherwise

259

"""

260

```

261

262

## Usage Patterns

263

264

### Data Cleaning Pipeline

265

266

```python

267

import phonenumbers

268

269

def clean_phone_number_data(raw_numbers, default_region="US"):

270

"""Clean a list of raw phone number strings for processing."""

271

cleaned_numbers = []

272

273

for raw_number in raw_numbers:

274

try:

275

# Step 1: Convert alpha characters

276

if phonenumbers.is_alpha_number(raw_number):

277

numeric_number = phonenumbers.convert_alpha_characters_in_number(raw_number)

278

else:

279

numeric_number = raw_number

280

281

# Step 2: Parse the number

282

parsed = phonenumbers.parse(numeric_number, default_region)

283

284

# Step 3: Validate

285

if phonenumbers.is_valid_number(parsed):

286

# Step 4: Normalize to E164 format

287

e164 = phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)

288

cleaned_numbers.append({

289

'original': raw_number,

290

'cleaned': e164,

291

'nsn': phonenumbers.national_significant_number(parsed),

292

'type': phonenumbers.number_type(parsed)

293

})

294

295

except phonenumbers.NumberParseException:

296

# Log invalid numbers for review

297

print(f"Could not parse: {raw_number}")

298

299

return cleaned_numbers

300

301

# Example usage

302

raw_data = [

303

"1-800-FLOWERS",

304

"+44 (20) 8366-1177",

305

"555.123.4567",

306

"(555) 123-4567 ext 123"

307

]

308

309

cleaned = clean_phone_number_data(raw_data)

310

for item in cleaned:

311

print(f"{item['original']} -> {item['cleaned']}")

312

```

313

314

### Deduplication Using Number Matching

315

316

```python

317

import phonenumbers

318

from phonenumbers import MatchType

319

320

def deduplicate_phone_numbers(number_strings, region="US"):

321

"""Remove duplicate phone numbers using intelligent matching."""

322

parsed_numbers = []

323

unique_numbers = []

324

325

# Parse all valid numbers

326

for number_str in number_strings:

327

try:

328

parsed = phonenumbers.parse(number_str, region)

329

if phonenumbers.is_valid_number(parsed):

330

parsed_numbers.append((number_str, parsed))

331

except phonenumbers.NumberParseException:

332

continue

333

334

# Find unique numbers

335

for original_str, parsed_num in parsed_numbers:

336

is_duplicate = False

337

338

for unique_str, unique_parsed in unique_numbers:

339

match_type = phonenumbers.is_number_match(parsed_num, unique_parsed)

340

341

if match_type in [MatchType.EXACT_MATCH, MatchType.NSN_MATCH]:

342

is_duplicate = True

343

break

344

345

if not is_duplicate:

346

unique_numbers.append((original_str, parsed_num))

347

348

return [num_str for num_str, _ in unique_numbers]

349

350

# Example usage

351

numbers_with_duplicates = [

352

"+1-800-555-1234",

353

"1 (800) 555-1234",

354

"18005551234",

355

"+1-555-123-4567",

356

"555.123.4567"

357

]

358

359

unique = deduplicate_phone_numbers(numbers_with_duplicates)

360

print(f"Original: {len(numbers_with_duplicates)} numbers")

361

print(f"Unique: {len(unique)} numbers")

362

```

363

364

### Number Structure Analysis

365

366

```python

367

import phonenumbers

368

369

def analyze_number_structure(number_str, region=None):

370

"""Analyze the internal structure of a phone number."""

371

try:

372

number = phonenumbers.parse(number_str, region)

373

374

if not phonenumbers.is_valid_number(number):

375

return {"error": "Invalid number"}

376

377

nsn = phonenumbers.national_significant_number(number)

378

area_code_length = phonenumbers.length_of_geographical_area_code(number)

379

ndc_length = phonenumbers.length_of_national_destination_code(number)

380

381

region_code = phonenumbers.region_code_for_number(number)

382

is_nanpa = phonenumbers.is_nanpa_country(region_code)

383

mobile_token = phonenumbers.country_mobile_token(number.country_code)

384

385

return {

386

'country_code': number.country_code,

387

'nsn': nsn,

388

'area_code_length': area_code_length,

389

'ndc_length': ndc_length,

390

'region': region_code,

391

'is_nanpa': is_nanpa,

392

'mobile_token': mobile_token,

393

'is_geographic': phonenumbers.is_number_geographical(number),

394

'can_dial_internationally': phonenumbers.can_be_internationally_dialled(number)

395

}

396

397

except phonenumbers.NumberParseException as e:

398

return {"error": str(e)}

399

400

# Example usage

401

analysis = analyze_number_structure("+442083661177")

402

print(f"Country code: {analysis['country_code']}")

403

print(f"NSN: {analysis['nsn']}")

404

print(f"Area code length: {analysis['area_code_length']}")

405

print(f"Region: {analysis['region']}")

406

```