Forum Discussion
Braille Translator Challenge
Using the Braille Alphabet lookup table provided:
- Part 1 - convert the given phrase from Braille to words.
- Part 2 - convert the given phrase from words to Braille.
✅ Solutions should be in the form of a single-cell formula.
✅ Generalized Lambda functions are welcome, provided the function definitions are included with your solution.
✅ Keep in mind, the "best" solution is not necessarily the shortest...
- Strive for conciseness, if you wish; just don't let your solutions be ordinary. 😉
Good luck and have fun!
14 Replies
- Patrick2788Silver Contributor
This discussion has inspired me to create a generalized function capable of locating a 2D array within a 2D array and returning a 2D array from the Moore neighborhood.
Excel has plenty of functions geared towards working with 1D arrays. Lookup functions return 1D arrays and shaping functions can return 2D arrays, but will only accept 1D array inputs.
What if the grid could be left as-is and a generalized function could still pull the return?
Consider this demo:
// Signature of draft Navigate2Dλ= LAMBDA( lookup_kernel, // 2D array (Will also accept 1D arrays) match_array_2D, // 2D arrays only [return_direction], // Moore neighborhood - 1 to 9. 5 is center. If omitted, defaults to 8 (below) [return_height], // If these two parameters are omitted, defaults to height x width of the lookup kernel [return_width],It's working smoothly so far. It's locating Cities using convolution windowing. I'll have more to share when it's finalized.
- m_tarlerSilver Contributor
So I thought of doing something similar. I'm not sure how yours works/acts so maybe mine is the same or maybe it is different. Again, my Excel Labs is still not working on this computer :( so here is the formula based:
GridLookUp, LAMBDA(pattern,lookUpGrid,[returnGrid],[not_found],[step_rows],[step_cols],[options], LET( InRows, ROWS(pattern), InCols, COLUMNS(pattern), OutputArray, IF((COLUMNS(lookUpGrid)=COLUMNS(returnGrid))*(ROWS(lookUpGrid)=ROWS(returnGrid)), 1, 0), OutRows, (ROWS(pattern)-1)*OutputArray+1, OutCols, (COLUMNS(pattern)-1)*OutputArray+1, NFout, IF(ISOMITTED(not_found),"PATTERN NOT FOUND",not_found), stepRows, IF(step_rows,step_rows,InRows), stepCols, IF(step_cols,step_cols,InCols), FindLocations, MAKEARRAY(QUOTIENT(ROWS(lookUpGrid)-InRows,stepRows)+1,QUOTIENT(COLUMNS(lookUpGrid)-InCols,stepCols)+1,LAMBDA(x,y, PRODUCT(--(TAKE(DROP(lookUpGrid,(x-1)*stepRows,(y-1)*stepCols),InRows,InCols)=pattern)))), XYcount, SEQUENCE(ROWS(FindLocations)*COLUMNS(FindLocations),,0), XYlist, IF({1,0},QUOTIENT(XYcount,COLUMNS(FindLocations)),MOD(XYcount,COLUMNS(FindLocations)))+1, XYLocations, IF(SUM(FindLocations),FILTER(XYlist, INDEX(FindLocations,TAKE(XYlist,,1),TAKE(XYlist,,-1))),0), Return, IF(COLUMNS(XYLocations)-1,DROP(REDUCE("",SEQUENCE(ROWS(XYLocations)),LAMBDA(p,q,HSTACK(p,TAKE(DROP(returnGrid,(INDEX(XYLocations,q,1)-1)*IF(OutputArray,stepRows,1),(INDEX(XYLocations,q,2)-1)*IF(OutputArray,stepCols,1)),OutRows,OutCols)))),,1),NFout), IF(ISOMITTED(returnGrid), FindLocations, IF(Return<>0,Return,"")) ))so the idea is that it can take a [pattern] to match in a [lookUpGrid] and then if [returnGrid] is given it will return the corresponding location in that grid. Note, if that [returnGrid] is NOT the same dimensions as the [lookUpGrid] it will return a single cell (e.g. brail dot grid (3x2) can lookup on brail dot grid array (3i x 2j) and return from a corresponding table of letters/characters (i x j) ). Also if [returnGrid] is not provided a grid of 0/1 will be returned corresponding to the location(s) where the search pattern was found. NOTE: this will return ALL matches for the pattern (arranged in a stacked row). Another feature is the [step_rows] and [step_cols] so you can tell the search to step by a specific amount. So in the example where there is a row of characters between each line you can use a [step_rows] of 4 to skip over those spacer lines. You can also force a [step] of 1 to 'scan' all possible combinations of the search array for that pattern. I've tested a few different combinations and it seems pretty robust. Let me know what you think.
BTW. on line 12 I am just creating a list of all possible x,y coordinates and tried using the trick i leaned below to use IF( c, QUOTIENT, MOD) (value1, value2) but I kept getting a memory error because I was trying to create a 2d array using c => {1,0}. It worked fine when I declared them fully inside the IF but doing that trick to pass them as functions didn't work in this array version.
And although it isn't a specific optional output, the XYLocations function returns a list of where the pattern is found in a 2 column row-col table of values.
Here is an example of using it to find the first character (S) from the Answer key. I rewrote the letters in a single spaced grid. But if you take the overlapping grids (so the [returnGrid] is B3:Q18) you would still get S out but also with the other 5 cells of the offset 3x2 grid as shown just to the right of the highlighted cell (C25:D27).
- djclementsSilver Contributor
My original (unedited) no Let, no Lambda solutions:
//Part 1: =CONCAT(XLOOKUP(BYROW(WRAPROWS(LEN(TOCOL(T5:BS7,,1)),6),CONCAT),BYROW(WRAPROWS(LEN(TOCOL((B4:Q6,B8:Q10,B12:Q14,B16:Q18),,1)),6),CONCAT),TOCOL((B3:Q3,B7:Q7,B11:Q11,B15:Q15),1))) //Part 2: =IF(--MID(CONCAT(XLOOKUP(MID(T16,SEQUENCE(LEN(T16)),1),TOCOL((B3:Q3,B7:Q7,B11:Q11,B15:Q15),1),BYROW(WRAPROWS(LEN(TOCOL((B4:Q6,B8:Q10,B12:Q14,B16:Q18),,1)),6),CONCAT))),WRAPCOLS(SEQUENCE(LEN(T16)*6),3),1),B4,"")I took advantage of the fact that TOCOL can accept a non-contiguous range reference, which it processes by area. The 3x2 Braille blocks are converted to 6-digit binary codes, which are used to perform the character lookup. In part 2, the lookup is reversed, whereby each character in the phrase is replaced with its corresponding 6-digit code, then MID is used with a sequence array to extract the individual binary digits and produce the 2D array.
My attempt at generalizing it (somewhat); plus, an alternative approach for part 2:
//Part 1: =BRA2TXT(T5:BS7, TOCOL((B3:Q3,B7:Q7,B11:Q11,B15:Q15),1), BRA2BIN((B4:Q6,B8:Q10,B12:Q14,B16:Q18))) //Part 2: =BRA2TXT(T16, TOCOL((B3:Q3,B7:Q7,B11:Q11,B15:Q15),1), BRA2BIN((B4:Q6,B8:Q10,B12:Q14,B16:Q18)), 1) //Where: BRA2BIN = LAMBDA(ref, BYROW(WRAPROWS(LEN(TOCOL(ref, , 1)), 6), CONCAT) ); BRA2TXT = LAMBDA(phrase,chr_array,bin_array,[text_to_braille], IF( text_to_braille, IF(--MID(XLOOKUP(MID(phrase, NUM3x2(phrase, 1), 1), chr_array, bin_array), NUM3x2(phrase), 1), "●", ""), CONCAT(XLOOKUP(BRA2BIN(phrase), bin_array, chr_array)) ) ); NUM3x2 = LAMBDA(txt,[opt], TRANSPOSE(1 + IF(opt, QUOTIENT, MOD)(SEQUENCE(LEN(txt) * 2, 3, 0), 6)) );In part 2 of this version, the text phrase is exploded in a Kronecker product fashion, whereby every character is extracted 6 times each, which in turn lookup their corresponding 6-digit codes in a 3x2 block. MID is then lifted over the resulting array, extracting just a single binary digit from each code.
- m_tarlerSilver Contributor
OK this is why I love this forum. I still need to dive into all these solutions more because I learn SO MUCH from all of you. But right off the back I can say I just learned:
a) TOCOL can take inputs like that
b) you can pass FUNCTIONS using an IF statement (i.e. on LINE 24: IF(opt, QUOTIENT, MOD)(SEQUENCE(LEN(txt) * 2, 3, 0), 6)
I will continue to review and I'm sure I will continue to learn. Thank you all.
- djclementsSilver Contributor
As it so happens, I also picked up both of these little nuggets from other contributors somewhere along the way. Community forums such as this are a wonderful way to share and learn from each other. <3
Cheers!
- Patrick2788Silver Contributor
I'm using a modular approach to solve both parts. You'll find all Lambdas in the AFE module of the workbook. The one I cannot share at present is Proto2Dλ, which is a generalized 2D lookup (Lookup values can be found in 2D array). I haven't finalized the function but it will be a part of my library in the future.
/* Part 1 =LET( // Re-tile 2D arrays, make phrase concise remap, BlockMapλ(Alphabet, 4, 2), ph, BYROW(BlockMapλ(Phrase, 3, 2), ARRAYTOTEXT), // Letters/characters from Braille alphabet letter, TAKE(remap, , 1), // Braille dots braille, BYROW(DROP(remap, , 2), ARRAYTOTEXT), // Lookup phrase, return letters, make consise result, CONCAT(XLOOKUP(ph, braille, letter)), ph ) Part 2 =LET( // Expansion kernels kernel, {0, 0; 1, 1; 2, 2}, kernel_b, {0, 1; 0, 1; 0, 1}, // Phrase ~ array ex, Explodeλ(phrase2), // Mesh grid i, MGridλ(Alphabet), j, MGridλ(Alphabet, 1), // 2D lookup: letter ~ alphabet ~ i/j x, 1 + Proto2Dλ(ex, Alphabet, i), y, Proto2Dλ(ex, Alphabet, j), // Expand indices using kernels r, Zoomλ(x, kernel), c, Zoomλ(y, kernel_b), // Get results final, TEXT(INDEX(Alphabet, r, c), ";;;@"), final ) */- djclementsSilver Contributor
I was hoping to see you here! Wonderful demonstration of your extended function library. I haven't had a chance to review them all in detail yet, but plan to soon. Looks amazing!
I think we followed the same basic logic for Part 1, with trivial variances in methods. Part 2 is very interesting... I'll have to take a closer look at the function definitions to see what's going on and keep an eye out for Proto2Dλ when it's ready.
Great work! :)
- m_tarlerSilver Contributor
This was fun. I created a few LAMBDA functions to work together:
BrailRows,WRAPROWS(TOROW(HSTACK(B4:Q6,B8:Q10,B12:Q14,B16:Q18),,1),6), BrailText,TAKE(WRAPROWS(HSTACK($B$3:$Q$3,$B$7:$Q$7,$B$11:$Q$11,$B$15:$Q$15),2),,1), Brail2Char,LAMBDA(dots,IF((ROWS(dots)<>3)+(COLUMNS(dots)<>2),"*",INDEX(BrailText,XMATCH(6,MMULT(--(TOROW(dots,,1)=BrailRows),SEQUENCE(6,,,0)))))), BrailLinesToText,LAMBDA(lines,IF((MOD(ROWS(lines),3)<>0)+(MOD(COLUMNS(lines),2)<>0),"*",CONCAT(MAKEARRAY(ROWS(lines)/3,COLUMNS(lines)/2,LAMBDA(r,c,Brail2Char(TAKE(DROP(lines,3*(r-1),2*(c-1)),3,2))))))), Char2Brail,LAMBDA(c,IFERROR(LET(a,WRAPCOLS(CHOOSEROWS(BrailRows,XMATCH(c,BrailText)),3),IF(a=0,"",a)),"*")), Line2Brail,LAMBDA(l,DROP(REDUCE("",MID(l,SEQUENCE(LEN(l)),1),LAMBDA(p,q,HSTACK(p,Char2Brail(q)))),,1)),I apologize I didn't clean this up by my Excel Labs plug-in isn't working right now...
Basically
BrailRows is just a reference for the brail answer key formatted as rows
BrailText is the corresponding character for each row (i.e. make it easy to add more characters)
Brail2Char will take a single grid of dots and convert it to the corresponding character
BrailLinesToText will take a whole line or even multiple lines of Brail and convert it to a single string output
Char2Brail will output the Brail grid of dots give a character
Line2Brail will take a string of characters and output the line of Brail dots
here is an image of the results:
and on the right side in cell BU is using the input from the gray area just to show it can take multi-line input
I see a couple other answers have been submitted that look interesting, I will have to check them out too.
- djclementsSilver Contributor
I'm loving the interconnected functions... far from ordinary indeed! You even went above and beyond with Part 1 by accepting a multi-line Braille input. Beautifully done!
Interesting choice to leave the lookup table as an array of flattened Braille blocks... I had considered that approach as well but ultimately went another route. I'll wait a little longer to see if anyone else wants to take a stab at this before sharing my solution.
Glad you enjoyed it! Thanks for playing. :)
- m_tarlerSilver Contributor
actually I initially converted each block into a binary number but decided that extra step wasn't needed. I see others used ARRAYTOTEXT and I considered that but I know excel does matrix operations very efficiently and although it makes the 'code' simplier, I wasn't sure if adding that would make it more or less efficient over all. Not that mine is any model of efficiency.
- IlirUIron Contributor
Hi djclements,
(I have not used the LAMBDA function in my formula)
I applied the first formula to cell AF10.
=LET( data, B3:Q18, phrase, T5:BS7, CONCAT(XLOOKUP(BYCOL(WRAPCOLS(BYCOL(phrase, ARRAYTOTEXT), 2), ARRAYTOTEXT), HSTACK(BYCOL(WRAPCOLS(BYCOL(CHOOSEROWS(data, {2,3,4}), ARRAYTOTEXT), 2), ARRAYTOTEXT), BYCOL(WRAPCOLS(BYCOL(CHOOSEROWS(data, {6,7,8}), ARRAYTOTEXT), 2), ARRAYTOTEXT), BYCOL(WRAPCOLS(BYCOL(CHOOSEROWS(data, {10,11,12}), ARRAYTOTEXT), 2), ARRAYTOTEXT), BYCOL(WRAPCOLS(BYCOL(CHOOSEROWS(data, {14,15,16}), ARRAYTOTEXT), 2), ARRAYTOTEXT)), HSTACK( TOROW(CHOOSEROWS(data, 1), 1), TOROW(CHOOSEROWS(data, 5), 1), TOROW(CHOOSEROWS(data, 9), 1), TOROW(CHOOSEROWS(data, 13), 1) ))))The second formula is the same as the first formula, but instead of T5:BS7, T19:BS21 should be inserted in the formula.
Try it and let me know.
IlirU
- djclementsSilver Contributor
Nice job! It works perfectly for Part 1, converting Braille blocks into words.
The reverse procedure for Part 2 may be slightly more difficult. Best of luck, if you dare! ;)
- askanCopper Contributor
Part1: =LET(b,HSTACK(B4:Q6,B8:Q10,B12:Q14,B16:Q18),a,IF(b="",0,b),c,HSTACK(CHAR(SEQUENCE(,26,97)),TOROW(F15:Q15,1)),l,LAMBDA(i,DROP(REDUCE(0,SEQUENCE(COLUMNS(i)/2,,,2),LAMBDA(x,y,HSTACK(x,CONCAT(CHOOSECOLS(i,y,y+1))))),,1)),CONCAT(XLOOKUP(l(IF(T5:BS7="",0,T5:BS7)),l(a),c))) Part2: =LET(b,HSTACK(B4:Q6,B8:Q10,B12:Q14,B16:Q18),a,IF(b="",0,b),c,HSTACK(CHAR(SEQUENCE(,26,97)),TOROW(F15:Q15,1)),l,LAMBDA(i,DROP(REDUCE(0,SEQUENCE(COLUMNS(i)/2,,,2),LAMBDA(x,y,HSTACK(x,CONCAT(CHOOSECOLS(i,y,y+1))))),,1)),d,REGEXEXTRACT(T16,".",1),e,XLOOKUP(d,c,l(a)),r,DROP(REDUCE(0,e,LAMBDA(x,y,HSTACK(x,WRAPROWS(REGEXEXTRACT(y,".",1),2)))),,1),SUBSTITUTE(r,0,))- djclementsSilver Contributor
I think I got you covered on LinkedIn already but well done once again! Nice job on both parts. :)