# Daily Challenge #222 - Parse Bank Account Numbers

You work for a bank, which has recently purchased an ingenious machine to assist in reading letters and faxes sent in by branch offices.

The machine scans the paper documents, and produces a string with a bank account that looks like this:

    _  _     _  _  _  _  _
| _| _||_||_ |_   ||_||_|
||_  _|  | _||_|  ||_| _|


... why did we buy this machine?

Anyway, your task is to write a function that can take bank account string and parse it into actual account numbers.

Example

 _  _  _  _  _  _  _  _  _
| | _| _|| ||_ |_   ||_||_|     => 23056789
|_||_  _||_| _||_|  ||_| _|


Tests

 _  _  _  _  _  _  _  _  _
|_| _| _||_||_ |_ |_||_||_|
|_||_  _||_| _||_| _||_| _|

_  _     _  _  _  _  _
| _| _||_||_ |_   ||_||_|
||_  _|  | _||_|  ||_| _|


Good luck!

This challenge comes from arhigod on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

### Discussion F# solution. The problem was badly specified, so here some assumptions:

1. Input comes as a string array, with each element representing one row, top to bottom.
2. Unrecognized characters will show up as ? in the output.
3. I decided not to strip the leading zero in the 023056789 example because that seems odd for a bank account number.
module DailyChallenge

[<Literal>]
let DigitRows = 3

[<Literal>]
let DigitCols = 3

let private isValidInput (input : string list) =
input.Length % DigitRows = 0
&& List.forall (fun (row : string) -> row.Length % DigitCols = 0) input

let private range current max =
let rStart = current * max
(rStart, rStart + max - 1)

let private parseChar =
function
| [ " _ "; "| |"; "|_|" ] -> "0"
| [ "   "; "  |"; "  |" ] -> "1"
| [ " _ "; " _|"; "|_ " ] -> "2"
| [ " _ "; " _|"; " _|" ] -> "3"
| [ "   "; "|_|"; "  |" ] -> "4"
| [ " _ "; "|_ "; " _|" ] -> "5"
| [ " _ "; "|_ "; "|_|" ] -> "6"
| [ " _ "; "  |"; "  |" ] -> "7"
| [ " _ "; "|_|"; "|_|" ] -> "8"
| [ " _ "; "|_|"; " _|" ] -> "9"
| _ -> "?"

let private parseInput (input : string list) =
seq {
for row in 0 .. input.Length / DigitRows - 1 do
let rStart, rEnd = range row DigitRows
for col in 0 .. input..Length / DigitCols - 1 do
let cStart, cEnd = range col DigitCols
yield input.[rStart..rEnd]
|> List.map (fun (row : string) -> row.[cStart..cEnd])
|> parseChar
}
|> System.String.Concat

let convert input =
if isValidInput input then Some(parseInput input) else None


Tests:

module DailyChallengeTests

open FsUnit.Xunit
open Xunit
open DailyChallenge

[<Fact>]
let 023056789() =
let rows =
[ " _  _  _  _  _  _  _  _  _ "
"| | _| _|| ||_ |_   ||_||_|"
"|_||_  _||_| _||_|  ||_| _|" ]
convert rows |> should equal (Some "023056789")

[<Fact>]
let 823856989() =
let rows =
[ " _  _  _  _  _  _  _  _  _ "
"|_| _| _||_||_ |_ |_||_||_|"
"|_||_  _||_| _||_| _||_| _|" ]
convert rows |> should equal (Some "823856989")

[<Fact>]
let 123456789() =
let rows =
[ "    _  _     _  _  _  _  _ "
"  | _| _||_||_ |_   ||_||_|"
"  ||_  _|  | _||_|  ||_| _|" ]
convert rows |> should equal (Some "123456789")

[<Fact>]
let invalid() =
let rows =
[ "    _  _     _  _  _  _  _ " ]
convert rows |> should equal None

[<Fact>]
let uncrecognized character() =
let rows =
[ "    _  _     _  _  _  _  _ "
"|   _| _||_||_ |_   ||_||_|"
"|  |_  _|  | _||_|  ||_| _|" ]
convert rows |> should equal (Some "?23456789")


Rust. Who needs error checking when you can support multiline instead?

use itertools::{izip, Itertools}; // 0.9.0
fn smol(big: &str) -> String {
big.lines()
.tuples()
.flat_map(|(l1, l2, l3)| {
izip!(l1.chars(), l2.chars(), l3.chars())
.tuples()
.map(|digit| match digit {
((' ', ' ', ' '), (' ', ' ', ' '), (' ', '|', '|')) => '1',
((' ', ' ', '|'), ('_', '_', '_'), (' ', '|', ' ')) => '2',
((' ', ' ', ' '), ('_', '_', '_'), (' ', '|', '|')) => '3',
((' ', '|', ' '), (' ', '_', ' '), (' ', '|', '|')) => '4',
((' ', '|', ' '), ('_', '_', '_'), (' ', ' ', '|')) => '5',
((' ', '|', '|'), ('_', '_', '_'), (' ', ' ', '|')) => '6',
((' ', ' ', ' '), ('_', ' ', ' '), (' ', '|', '|')) => '7',
((' ', '|', '|'), ('_', '_', '_'), (' ', '|', '|')) => '8',
((' ', '|', ' '), ('_', '_', '_'), (' ', '|', '|')) => '9',
x => unreachable!(),
})
})
.collect()
}

fn main() {
println!(
"{}",
smol(
r"    _  _     _  _  _  _  _
| _| _||_||_ |_   ||_||_|
||_  _|  | _||_|  ||_| _|
_  _     _  _  _  _  _
| _| _||_||_ |_   ||_||_|
||_  _|  | _||_|  ||_| _|
_  _     _  _  _  _  _
| _| _||_||_ |_   ||_||_|
||_  _|  | _||_|  ||_| _|"
)
);
}


Look at it go

Python solution

# stores the modified string representation of a digit
digitStringDict = {
" _ | ||_|" : "0",
"     |  |" : "1",
" _  _||_ " : "2",
" _  _| _|" : "3",
"   |_|  |" : "4",
" _ |_  _|" : "5",
" _ |_ |_|" : "6",
" _   |  |" : "7",
" _ |_||_|" : "8",
" _ |_| _|" : "9"
}

# takes a multiline string as input
# outputs the int representation of that
def stringToAccountNumber(account: str) -> int:
account = account.strip("\n")
parts = account.split("\n") # split the actual string into three strings based on new-line character
numberStr = "" # stores the string representation of the number

for i in range(0,len(parts),3):
numberStr += digitStringDict[parts[i:i+3]+parts[i:i+3]+parts[i:i+3]]

return int(numberStr)


Output,

test1 = '''
_  _  _  _  _  _  _  _  _
|_| _| _||_||_ |_ |_||_||_|
|_||_  _||_| _||_| _||_| _|

'''

test2 = '''
_  _     _  _  _  _  _
| _| _||_||_ |_   ||_||_|
||_  _|  | _||_|  ||_| _|

'''

test3 = '''
_  _  _  _  _  _  _  _  _
| | _| _|| ||_ |_   ||_||_|
|_||_  _||_| _||_|  ||_| _|

'''

print(stringToAccountNumber(test1)) # output -> 823856989
print(stringToAccountNumber(test2)) # output -> 123456789
print(stringToAccountNumber(test3)) # output -> 23056789


This is a rather hacky Python solution that relies on a dictionary of strings mapping to digits. I made the assumption that input would come as an array of strings, one for each level of the digits.

## Code

digit_dict = {" _ | | _ ": 0, "     |  |": 1,
" _  _||_ ": 2, " _  _| _|": 3,
"   |_|  |": 4, " _ |_  _|": 5,
" _ |_ |_|": 6, " _   |  |": 7,
" _ |_||_|": 8, " _ |_| _|" : 9}

def parse_bank_code(str_list, digit_dict):
"""
Function that takes awkward digit string and and digit dict;
returns a normal string of digits.
"""

digits = []  # Holder for digits

# Loop through three places at a time
for i in range(0, len(str_list), 3):

# Take three characters from each level as a list
digit = [x[i:i+3] for x in str_list]

# Join the characters together and get the dict value
digit = digit_dict["".join(digit)]

# Add the digit as a string to the list
digits.append(str(digit))

# Return the final code
return "".join(digits)  