Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations bkrike on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

SOUNDS-LIKE : VFP METAPHONE IMPLEMENTATION 2

Status
Not open for further replies.

craigsboyd

IS-IT--Management
Nov 9, 2002
2,839
US
In my applications I like to provide the user a way to search for names in the database via "sounds-like". this is especially helpful when the user doesn't know the exact spelling. There are many algorithms around for doing this (soundex, metaphone, double-metaphone, phonix, q-gram, etc.) In VFP the only native way of doing this is using the SOUNDEX() or DIFFERENCE() functions, but I have found them to be less accurate than I would like. The worst thing about them is there reliance on the first letter of the word or name. So here is a version of the metaphone algorithm translated to VFP (Please note: I hope to port most of the popular sounds-like algorithms to VFP at some point).

Cut-N-Paste the fully working example code below into a prg file and run it from within VFP.

Code:
*********************************
*!* Example of use
*********************************
MESSAGEBOX(["FONE" compared to "PHONE"] + CHR(13)+ CHR(13) ;
	+"SOUNDEX RETURNS: " + IIF(SOUNDEX("FONE") = SOUNDEX("PHONE"),"TRUE", "FALSE") + CHR(13)+ CHR(13) ;
	+"METAPHONE RETURNS: " + IIF(Metaphone("FONE") = Metaphone("PHONE"),"TRUE", "FALSE"),0,"EXAMPLE 1 OF 2")

MESSAGEBOX(["KREG" compared to "CRAIG"] + CHR(13)+ CHR(13) ;
	+"SOUNDEX RETURNS: " + IIF(SOUNDEX("KREG") = SOUNDEX("CRAIG"),"TRUE", "FALSE") + CHR(13)+ CHR(13) ;
	+"METAPHONE RETURNS: " + IIF(Metaphone("KREG") = Metaphone("CRAIG"),"TRUE", "FALSE"),0,"EXAMPLE 2 OF 2")

DO WHILE .T.
	IF MESSAGEBOX("Would you like to try a comparison of your own?",36,"GIVE IT A TRY") = 6
		cFirstWord = ALLTRIM(INPUTBOX("Enter a name or word:", "FIRST WORD TO COMPARE"))
		IF EMPTY(cFirstWord)
			MESSAGEBOX("You must enter both words.", 16, "UNABLE TO RUN COMPARISON")
			LOOP
		ENDIF
		cSecondWord =  ALLTRIM(INPUTBOX("Enter another name or word:", "SECOND WORD TO COMPARE"))
		IF EMPTY(cSecondWord)
			MESSAGEBOX("You must enter both words.", 16, "UNABLE TO RUN COMPARISON")
			LOOP
		ENDIF
		MESSAGEBOX(["]+cFirstWord+[" compared to "]+cSecondWord+["] + CHR(13)+ CHR(13) ;
			+"SOUNDEX RETURNS: " + IIF(SOUNDEX(cFirstWord) = SOUNDEX(cSecondWord),"TRUE", "FALSE") + CHR(13)+ CHR(13) ;
			+"METAPHONE RETURNS: " + IIF(Metaphone(cFirstWord) = Metaphone(cSecondWord),"TRUE", "FALSE"),0,"COMPARISON RESULTS")
	ELSE
		EXIT
	ENDIF
ENDDO
*********************************

*!* Original C version by Michael Kuhn <rhlab!mkuhn@uunet.uu.net>
*!* [URL unfurl="true"]http://aspell.sourceforge.net/metaphone/metaphone-kuhn.txt[/URL]
*!* Metaphone algorithm translated from C to Delphi by Tom White <wcs@lnellex.com>
*!* Translated to Visual Basic by Dave White 9/10/01
*!* Translated to Visual Foxpro by Craig Boyd (Slighthaze) 10-21-2003 craig1442@mchsi.com

*********************************
FUNCTION Metaphone (lcWord)
*********************************
#DEFINE VOWELS		&quot;AEIOU&quot;
#DEFINE FRONTV		&quot;EIY&quot;
#DEFINE VARSON		&quot;CSPTG&quot;
#DEFINE DBL		&quot;.&quot;
#DEFINE EXCPPAIR	&quot;AGKPW&quot;
#DEFINE NXTLTR		&quot;ENNNR&quot;
#DEFINE ALPHACHR	&quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&quot;

LOCAL lcB, lcC, lcInp, lcOutp, lnIi, lnJj
LOCAL lcCurltr, lcPrevltr, lcNextltr, lcNextltr2, lcNextltr3
LOCAL lnVowelafter, llVowelbefore, llFrontvafter, llSilent, llHard

IF PCOUNT()=0
	RETURN &quot;&quot;
ELSE
	IF TYPE(&quot;lcWord&quot;) != &quot;C&quot;
		RETURN &quot;&quot;
	ENDIF
ENDIF

lcInp = UPPER(lcWord)
lcInp = CHRTRAN(lcInp,CHRTRAN(lcInp,ALPHACHR,&quot;&quot;),&quot;&quot;) &&Remove all non-alpha characters
lcOutp = &quot;&quot;

IF LEN(lcInp) = 0
	RETURN &quot;&quot;
ENDIF

*!*--Check rules at beginning of word
IF LEN(lcInp) > 1
	lcB = SUBSTR(lcInp, 1, 1)
	lcC = SUBSTR(lcInp, 2, 1)
	lnIi = ATC(lcB, EXCPPAIR)
	lnJj = ATC(lcC, NXTLTR)
	IF lnIi = lnJj AND lnIi > 0
		lcInp = SUBSTR(lcInp, 2, LEN(lcInp) - 1)
	ENDIF
ENDIF
IF SUBSTR(lcInp, 1, 1) = &quot;X&quot;
	STUFF(lcInp, 1, 1) = &quot;S&quot;
ENDIF
IF SUBSTR(lcInp, 1, 2) = &quot;WH&quot;
	lcInp = &quot;W&quot; + SUBSTR(lcInp, 3)
ENDIF
IF RIGHT(lcInp, 1) = &quot;S&quot;
	lcInp = LEFT(lcInp, LEN(lcInp) - 1)
ENDIF
lnIi = 0
DO WHILE (lnIi > LEN(lcInp))
	lnIi = lnIi + 1
*!*--Main LOOP!
	llSilent = .F.
	llHard = .F.
	lcCurltr = SUBSTR(lcInp, lnIi, 1)
	llVowelbefore = .F.
	lcPrevltr = &quot; &quot;
	IF lnIi > 1
		lcPrevltr = SUBSTR(lcInp, lnIi - 1, 1)
		IF InStrC(lcPrevltr, VOWELS) > 0
			llVowelbefore = .T.
		ENDIF
	ENDIF
	IF ((lnIi = 1) AND (InStrC(lcCurltr, VOWELS) > 0))
		lcOutp = lcOutp + lcCurltr
		LOOP
	ENDIF
	llVowelafter = .F.
	llFrontvafter = .F.
	lcNextltr = &quot; &quot;
	IF lnIi < LEN(lcInp)
		lcNextltr = SUBSTR(lcInp, lnIi + 1, 1)
		IF InStrC(lcNextltr, VOWELS) > 0
			llVowelafter = .T.
		ENDIF
		IF InStrC(lcNextltr, FRONTV) > 0
			llFrontvafter = .T.
		ENDIF
	ENDIF
*!*--Skip double letters EXCEPT ones in variable double
	IF InStrC(lcCurltr, DBL) = 0
		IF lcCurltr = lcNextltr
			LOOP
		ENDIF
	ENDIF
	lcNextltr2 = &quot; &quot;
	IF LEN(lcInp) - lnIi > 1
		lcNextltr2 = SUBSTR(lcInp, lnIi + 2, 1)
	ENDIF
	lcNextltr3 = &quot; &quot;
	IF (LEN(lcInp) - lnIi) > 2
		lcNextltr3 = SUBSTR(lcInp, lnIi + 3, 1)
	ENDIF
	DO CASE
	CASE lcCurltr = &quot;B&quot;
		llSilent = .F.
		IF (lnIi = LEN(lcInp)) AND (lcPrevltr = &quot;M&quot;)
			llSilent = .T.
		ENDIF
		IF NOT (llSilent)
			lcOutp = lcOutp + lcCurltr
		ENDIF
	CASE lcCurltr = &quot;C&quot;
		IF NOT ((lnIi > 2) AND (lcPrevltr = &quot;S&quot;) AND llFrontvafter)
			IF ((lnIi > 1) AND (lcNextltr = &quot;I&quot;) AND (lcNextltr2 = &quot;A&quot;))
				lcOutp = lcOutp + &quot;X&quot;
			ELSE
				IF llFrontvafter
					lcOutp = lcOutp + &quot;S&quot;
				ELSE
					IF ((lnIi > 2) AND (lcPrevltr = &quot;S&quot;) AND (lcNextltr = &quot;H&quot;))
						lcOutp = lcOutp + &quot;K&quot;
					ELSE
						IF lcNextltr = &quot;H&quot;
							IF ((lnIi = 1) AND (InStrC(lcNextltr2, VOWELS) = 0))
								lcOutp = lcOutp + &quot;K&quot;
							ELSE
								lcOutp = lcOutp + &quot;X&quot;
							ENDIF
						ELSE
							IF lcPrevltr = &quot;C&quot;
								lcOutp = lcOutp + &quot;C&quot;
							ELSE
								lcOutp = lcOutp + &quot;K&quot;
							ENDIF
						ENDIF
					ENDIF
				ENDIF
			ENDIF
		ENDIF
	CASE lcCurltr = &quot;D&quot;
		IF ((lcNextltr = &quot;G&quot;) AND (InStrC(lcNextltr2, FRONTV) > 0))
			lcOutp = lcOutp + &quot;J&quot;
		ELSE
			lcOutp = lcOutp + &quot;T&quot;
		ENDIF
	CASE lcCurltr = &quot;G&quot;
		llSilent = .F.
		IF ((lnIi < LEN(lcInp)) AND (lcNextltr = &quot;H&quot;) AND (InStrC(lcNextltr2, VOWELS) = 0))
			llSilent = .T.
		ENDIF
		DO CASE
		CASE lcCurltr = ((lnIi = LEN(lcInp) - 4) AND (lcNextltr = &quot;N&quot;) AND (lcNextltr2 = &quot;E&quot;) AND (lcNextltr3 = &quot;D&quot;))
			llSilent = .T.
		CASE lcCurltr = ((lnIi = LEN(lcInp) - 2) AND (lcNextltr = &quot;N&quot;))
			llSilent = .T.
		ENDCASE
		IF (lcPrevltr = &quot;D&quot;) AND llFrontvafter
			llSilent = .T.
		ENDIF
		IF lcPrevltr = &quot;G&quot;
			llHard = .T.
		ENDIF
		IF NOT (llSilent)
			IF llFrontvafter AND (NOT (llHard))
				lcOutp = lcOutp + &quot;J&quot;
			ELSE
				lcOutp = lcOutp + &quot;K&quot;
			ENDIF
		ENDIF
	CASE lcCurltr = &quot;H&quot;
		llSilent = .F.
		IF InStrC(lcPrevltr, VARSON) > 0
			llSilent = .T.
		ENDIF
		IF llVowelbefore AND (NOT (llVowelafter))
			llSilent = .T.
		ENDIF
		IF NOT llSilent
			lcOutp = lcOutp + lcCurltr
		ENDIF
	CASE lcCurltr = &quot;F&quot; OR lcCurltr =  &quot;J&quot; OR lcCurltr =  &quot;L&quot; OR lcCurltr =  &quot;M&quot; OR lcCurltr =  &quot;N&quot; OR lcCurltr =  &quot;R&quot;
		lcOutp = lcOutp + lcCurltr
	CASE lcCurltr = &quot;K&quot; IF lcPrevltr <> &quot;C&quot;
		lcOutp = lcOutp + lcCurltr
	CASE lcCurltr = &quot;P&quot;
		IF lcNextltr = &quot;H&quot;
			lcOutp = lcOutp + &quot;F&quot;
		ELSE
			lcOutp = lcOutp + &quot;P&quot;
		ENDIF
	CASE lcCurltr = &quot;Q&quot;
		lcOutp = lcOutp + &quot;K&quot;
	CASE lcCurltr = &quot;S&quot;
		IF ((lnIi > 2) AND (lcNextltr = &quot;I&quot;) AND ((lcNextltr2 = &quot;O&quot;) OR (lcNextltr2 = &quot;A&quot;)))
			lcOutp = lcOutp + &quot;X&quot;
		ENDIF
		IF (lcNextltr = &quot;H&quot;)
			lcOutp = lcOutp + &quot;X&quot;
		ELSE
			lcOutp = lcOutp + &quot;S&quot;
		ENDIF
	CASE lcCurltr = &quot;T&quot;
		IF ((lnIi > 0) AND (lcNextltr = &quot;I&quot;) AND ((lcNextltr2 = &quot;O&quot;) OR (lcNextltr2 = &quot;A&quot;)))
			lcOutp = lcOutp + &quot;X&quot;
		ENDIF
		DO CASE
		CASE lcNextltr = &quot;H&quot;
			IF ((lnIi > 1) OR (InStrC(lcNextltr2, VOWELS) > 0))
				lcOutp = lcOutp + &quot;0&quot;
			ELSE
				lcOutp = lcOutp + &quot;T&quot;
			ENDIF
		CASE NOT ((lnIi < LEN(lcInp) - 3) AND (lcNextltr = &quot;C&quot;) AND (lcNextltr2 = &quot;H&quot;))
			lcOutp = lcOutp + &quot;T&quot;
		ENDCASE
	CASE lcCurltr = &quot;V&quot;
		lcOutp = lcOutp + &quot;F&quot;
	CASE lcCurltr = &quot;W&quot; OR lcCurltr =  &quot;Y&quot;
		IF (lnIi < LEN(lcInp) - 1) AND llVowelafter
			lcOutp = lcOutp + lcCurltr
		ENDIF
	CASE lcCurltr = &quot;X&quot;
		lcOutp = lcOutp + &quot;KS&quot;
	CASE lcCurltr = &quot;Z&quot;
		lcOutp = lcOutp + &quot;S&quot;
	ENDCASE
ENDDO
RETURN lcOutp
ENDFUNC

*********************************
FUNCTION InStrC (lcSearchIn, lcSoughtCharacters)
*********************************
*!*--- Returns the position of the first character in lcSearchIn that is contained
*!*--- in the string lcSoughtCharacters.  Returns 0 if none found.
LOCAL i, lnReturn
lnReturn = 0
lcSoughtCharacters = UPPER(lcSoughtCharacters)
FOR i = 1 TO LEN(lcSearchIn)
	IF ATC(SUBSTR(lcSearchIn, i, 1), lcSoughtCharacters) > 0
		lnReturn = i
		EXIT
	ENDIF
ENDFOR
RETURN lnReturn
ENDFUNC

Slighthaze = NULL
craig1442@mchsi.com
&quot;Whom computers would destroy, they must first drive mad.&quot; - Anon​
 
This is my first time for me to submit.:)
I start using vfp 3 years ago and I have coded some program for my work ,especialy data validation system.
actually I have to do this because of vfp's soundex() is
not support my language(thai).more over my language
have 44 char and many vowell...not only 26 char like
english.
This program will check similarity by character base
not sound between 2 files.by using firstname and lastname
as keyword you can change setting in part 4 and let's see the
result.By the way I never try this with english.
Last,this program need a lot of resource,be aware


*********************************************************************
* This program was created by boonchai chaiyasirinroje 2001 *
* if you have comment or suggestion please feel free to let me know *
* if you found any bug please kindly tell me at papaaui@yahoo.com *
*********************************************************************
*1.open file********************************************
CLOS TABLE ALL
SET SAFETY OFF
SET ECHO OFF
SET TALK OFF

USE &quot;c:\validation_system\source\FileA.dbf&quot; EXCLUSIVE
USE &quot;c:\validation_system\source\FileB.dbf&quot; EXCLUSIVE
SET DEFAULT TO &quot;C:\validation_system\result&quot;

*2.make keyfield********************************************
SELECT ALLT(firstname2)+ &quot; &quot; + ALLT(lastname2) AS keyfield FROM tb240103_1999UP WHERE !'(' $ keyfield GROUP BY keyfield ORDER BY keyfield INTO TABLE tmpKeyField
nTotalRecA = _TALLY
brow
SELECT ALLT(firstname2)+ &quot; &quot; + ALLT(lastname2) AS keyfield FROM tb240103_1999UP WHERE '(' $ keyfield GROUP BY keyfield ORDER BY keyfield INTO TABLE tmpKeyField2
nTotalRecA2 = _TALLY
brow
SELECT * FROM tmpKeyField2 INTO ARRAY aA01012
SELECT ALLT(fname)+ &quot; &quot; + ALLT(lname) AS keyfield FROM afb240103_2002_smearpos_groupLF WHERE !'(' $ keyfield GROUP BY keyfield ORDER BY keyfield INTO TABLE tmpKeyField3
nTotalRecB = _TALLY
brow
SELECT * FROM tmpKeyField3 INTO ARRAY aB0101
SELECT ALLT(fname)+ &quot; &quot; + ALLT(lname) AS keyfield FROM afb240103_2002_smearpos_groupLF WHERE '(' $ keyfield GROUP BY keyfield ORDER BY keyfield INTO TABLE tmpKeyField4
nTotalRecB2 = _TALLY
brow
SELECT * FROM tmpKeyField4 INTO ARRAY aB01012
*return
*3.analyse table********************************************
SELECT tmpKeyField
cCalFieldsA = 'LEN(ALLT(keyfield))'
CALCULATE AVG(&cCalFieldsA ), MIN(&cCalFieldsA ), MAX(&cCalFieldsA ),STD(&cCalFieldsA ) ;
TO nAvgA,nMinA,nMaxA,nStdA
SELECT tmpKeyField3
cCalFieldsB = 'LEN(ALLT(keyfield))'
CALCULATE AVG(&cCalFieldsB ), MIN(&cCalFieldsB ), MAX(&cCalFieldsB ),STD(&cCalFieldsB ) ;
TO nAvgB,nMinB,nMaxB,nStdB

MESSAGEBOX(&quot;nTotalRecA = &quot; + ALLT(STR(nTotalRecA)) + CHR(13) +;
&quot;nAvgA = &quot;+ ALLT(STR(nAvgA))+ CHR(13)+ ;
&quot;nMinA = &quot;+ ALLT(STR(nMinA))+ CHR(13)+ ;
&quot;nMaxA = &quot;+ ALLT(STR(nMaxA))+ CHR(13)+ ;
&quot;nStdA = &quot;+ ALLT(STR(nStdA))+ CHR(13)+ ;
&quot;nTotalRecB = &quot; + ALLT(STR(nTotalRecB)) + CHR(13) +;
&quot;nAvgB = &quot;+ ALLT(STR(nAvgB))+ CHR(13)+ ;
&quot;nMinB = &quot;+ ALLT(STR(nMinB))+ CHR(13)+ ;
&quot;nMaxB = &quot;+ ALLT(STR(nMaxB))+ CHR(13)+ ;
&quot;nStdB = &quot;+ ALLT(STR(nStdB)))

*4.setting - you can try to change some variable to see the result*****
***I think,name of variables tell function of themself already *******
nError = ROUND(nStdA,0)
nErrLen = nError
nErrLenFname = 2
nErrLenLname = 3
nPerLike = 69
nPerLikeFname = 30
nPerLikeLname = 30
nRecStart = 1
nRecEnd = nTotalRecB
tblResultIn = 'TB_In'
tblResultXIn = 'TB_XIn'
*5.prepare array from table********************************************
CREATE CURSOR curSumTable (no N(7), value N(7),percent N(5,2),subarray N(2))
DIMENSION aSumTable[nMaxA,4]
FOR i = 1 TO nMaxA
aSumTable[i,1] = i
ENDFOR
FOR i = 1 TO nMaxA
SELECT tmpKeyField
COUNT FOR LEN(ALLT(keyfield)) = i TO nLenCount
aSumTable[i,2] = nLenCount
ENDFOR
FOR i = 1 TO nMaxA
aSumTable[i,3] = (aSumTable[i,2])*100/nTotalRecA
ENDFOR

FOR i = 1 TO nMaxA
nRowPerBlock = FLOOR(65000/(i + nError))
aSumTable[i,4] = CEILING(aSumTable[i,2]/nRowPerBlock)
ENDFOR
SELECT curSumTable
APPEND FROM ARRAY aSumTable
nMaxCurSumTable = RECCOUNT()
BROW

FOR i = 1 TO nMaxA
IF aSumTable[i,4] <> 0 && this character lenght have some data
SELECT * FROM tmpKeyField WHERE LEN(ALLT(keyfield)) = i INTO TABLE curKeyField
SELECT curKeyField
ALTER TABLE curKeyField ALTER COLUMN KeyField C(i)
nFieldCount = i + nError - 1
FOR nFiledName = 1 TO nFieldCount
cFieldName = 'field'+ ALLT(STR(nFiledName))
nFieldWidth = (nError*2)+2
ALTER TABLE curKeyField ADD COLUMN &cFieldName C(nFieldWidth) NULL
ENDFOR
FOR nFieldPos = 1 TO nFieldCount
DO CASE
CASE nFieldPos <= nError
nStartSubStr = 1
nNumSubStr = ',nFieldPos+1+nError'
CASE nFieldPos > nError AND nFieldPos < i-nError
nStartSubStr = nFieldPos - nError
nNumSubStr = ',(nError*2)+2'
OTHERWISE
nStartSubStr = nFieldPos - nError
nNumSubStr = ''
ENDCASE
cSTR = 'SUBSTR(keyfield,nStartSubStr'+ nNumSubStr +')'
cFieldName = FIELD(nFieldPos+1) && shift 1 column because 1 column is whole keyfield
REPLACE &cFieldName WITH &cSTR ALL

ENDFOR
*SET SAFETY OFF
IF aSumTable[i,4] = 1
cArrayName = 'aA' + IIF (i<10,'0'+ALLT(STR(i)),ALLT(STR(i)) )+ '01'
PUBLIC &cArrayName [aSumTable[i,2]]
SELECT * FROM curKeyField INTO ARRAY &cArrayName
ELSE
FOR isub = 1 TO aSumTable[i,4]
nStartPoint = IIF(isub = 1 , 1,(nRowPerBlock*(isub-1))+1)
nEndPoint = nStartPoint + nRowPerBlock -1
cArrayName = 'aA' + IIF (i<10,'0'+ALLT(STR(i)),ALLT(STR(i)))+ IIF (isub<10,'0'+ALLT(STR(isub)),ALLT(STR(isub)))
PUBLIC &cArrayName [nRowPerBlock]
SELECT * FROM curKeyField WHERE LEN(ALLT(keyfield)) = i AND BETWEEN(RECNO(),nStartPoint,nEndPoint) INTO ARRAY &cArrayName
ENDFOR
ENDIF
ENDIF
ENDFOR

***start loop**********
*MESSAGEBOX('START')
nTimeStart = DATETIME()
CREATE TABLE &tblResultIn (no_rec N(7), no_fromB N(7) , no_group N(7) , no_each N(3), KeyFieldA C(35) , KeyFieldB C(35) , nPB2A N(3) , nPFB2A N(3) , nPFA2B N(3), nPLB2A N(3) , nPLA2B N(3) , percent N(3))
CREATE TABLE &tblResultXIn (no_rec N(7), no_fromB N(7) , no_group N(7) , no_each N(3), KeyFieldA C(35) , KeyFieldB C(35) , nPB2A N(3) , nPFB2A N(3) , nPFA2B N(3), nPLB2A N(3) , nPLA2B N(3) , percent N(3))
nNo_recIn = 0
nNo_recXIn = 0
nGroupIn = 1
bFound = .F.
FOR nArrayB = nRecStart TO nRecEnd
nPerLikeBtoA =0
nPerLikeFnameBtoA =0
nPerLikeFnameAtoB =0
nPerLikeLnameBtoA =0
nPerLikeLnameAtoB =0

nEach = 0
B_NAME = ALLT(aB0101[nArrayB])
B_LEN = LEN(B_NAME)
nLenghLow = B_LEN - nErrLen
nLenghHi = B_LEN + nErrLen
****************************************START CHK GROUP &quot;NO ()&quot;
FOR nArray = nLenghLow TO nLenghHi
SELECT curSumTable
IF nArray > nMaxCurSumTable && LONGER THAN IN TABLE
EXIT
ENDIF
IF nArray <= 0 && LESS THAN 0 GO BACK TO LOOP
LOOP
ENDIF
GO nArray && GO TO THAT RECORD
IF value = 0 && DON'T HAVE GO BACK TO LOOP
LOOP
ELSE
cNO = ALLT(STR(nArray))
FOR nLoopSub = 1 TO subarray
cArrayName = 'aA' + IIF (nArray < 10,'0'+cNO,cNO )+ IIF (nLoopSub < 10,'0'+ALLT(STR(nLoopSub)) ,ALLT(STR(nLoopSub)))
nTotalArray = ALEN(&cArrayName,1)
FOR j = 1 TO nTotalArray
B_POS = 1
B_NUM = 0
DO WHILE B_LEN > B_POS
B_VAL = SUBSTR(B_NAME,B_POS,2)
A_STR = &cArrayName[j,B_POS+1]
B_NUM = IIF (B_VAL $ A_STR,B_NUM + 1,B_NUM)
B_POS = B_POS+1
ENDDO
nPerLikeBtoA = B_NUM/(B_LEN-1)*100

IF nPerLikeBtoA > nPerLike
*************************
A_NAME = ALLT(&cArrayName[j,1])
A_LEN = LEN(A_NAME)
nSpacePosA = AT(&quot; &quot;,A_NAME)
nSpacePosB = AT(&quot; &quot;,B_NAME)

cFnameA = SUBSTR(A_NAME,1,nSpacePosA -1)
cFnameB = SUBSTR(B_NAME,1,nSpacePosB -1)
cLnameA = SUBSTR(A_NAME,nSpacePosA + 1)
cLnameB = SUBSTR(B_NAME,nSpacePosB + 1)

nLenFnameA = LEN(cFnameA )
nLenFnameB = LEN(cFnameB )
nLenLnameA = LEN(cLnameA )
nLenLnameB = LEN(cLnameB )

IF BETWEEN(nLenFnameB , nLenFnameA - nErrLenFname ,nLenFnameA + nErrLenFname )
B_POS = 1
B_NUM = 0
DO WHILE (nLenFnameB - B_POS) >0
B_VAL = SUBSTR(cFnameB ,B_POS,2)
B_NUM = IIF (B_VAL $ cFnameA ,B_NUM + 1,B_NUM)
B_POS = B_POS+1
ENDDO
nPerLikeFnameBtoA = (B_NUM/(nLenFnameB -1))*100
IF nPerLikeFnameBtoA > nPerLikeFname
A_POS = 1
A_NUM = 0
DO WHILE (nLenFnameA - A_POS) > 0
A_VAL = SUBSTR(cFnameA ,A_POS,2)
A_NUM = IIF (A_VAL $ cFnameB ,A_NUM + 1,A_NUM)
A_POS = A_POS+1
ENDDO
nPerLikeFnameAtoB = (A_NUM/(nLenFnameA -1))*100
IF nPerLikeFnameAtoB > nPerLikeFname
IF BETWEEN(nLenLnameB , nLenLnameA - nErrLenLname ,nLenLnameA + nErrLenLname )
B_POS = 1
B_NUM = 0
DO WHILE (nLenLnameB - B_POS) >0
B_VAL = SUBSTR(cLnameB ,B_POS,2)
B_NUM = IIF (B_VAL $ cLnameA ,B_NUM + 1,B_NUM)
B_POS = B_POS+1
ENDDO
nPerLikeLnameBtoA = (B_NUM/(nLenLnameB -1))*100
IF nPerLikeLnameBtoA > nPerLikeLname
A_POS = 1
A_NUM = 0
DO WHILE (nLenLnameA - A_POS) >0
A_VAL = SUBSTR(cLnameA ,A_POS,2)
A_NUM = IIF (A_VAL $ cLnameB ,A_NUM + 1,A_NUM)
A_POS = A_POS+1
ENDDO
nPerLikeLnameAtoB = (A_NUM/(nLenLnameA -1))*100
nPercent = ((nPerLikeFnameBtoA + nPerLikeFnameBtoA )/2 + (nPerLikeLnameBtoA + nPerLikeLnameAtoB)/2)/2
IF nPerLikeLnameAtoB > nPerLikeLname
*!* MESSAGEBOX( 'nPerLikeBtoA = ' + STR(nPerLikeBtoA) + CHR(13)+ ;
*!* 'nPerLikeFnameBtoA = '+ STR(nPerLikeFnameBtoA) + CHR(13)+ ;
*!* 'nPerLikeFnameAtoB = '+ STR(nPerLikeFnameAtoB) + CHR(13)+ ;
*!* 'nPerLikeLnameBtoA = '+ STR(nPerLikeLnameBtoA) + CHR(13)+ ;
*!* 'nPerLikeLnameAtoB = '+ STR(nPerLikeLnameAtoB) + CHR(13)+ ;
*!* 'nPercent = '+ STR(nPercent) )



nEach = nEach + 1
bFound = .T.
SELECT &tblResultIn
APPEND BLANK
nNo_recIn = nNo_recIn +1
REPLACE no_rec with nNo_recIn ,;
no_fromB with nArrayB ,;
no_each with nEach ,;
no_group with nGroupIn ,;
KeyFieldA with A_NAME ,;
KeyFieldB with B_NAME ,;
nPB2A with nPerLikeBtoA ,;
nPFB2A with nPerLikeFnameBtoA ,;
nPFA2B with nPerLikeFnameAtoB ,;
nPLB2A with nPerLikeLnameBtoA ,;
nPLA2B with nPerLikeLnameAtoB ,;
percent with nPercent

brow nowait

ENDIF
ENDIF
ENDIF
***********
ENDIF
ENDIF
ENDIF

*************************
ENDIF
ENDFOR

ENDFOR

ENDIF
ENDFOR

IF bFound = .F.
SELECT &tblResultXIn
APPEND BLANK
nNo_recXIn = nNo_recXIn +1
REPLACE no_rec with nNo_recXIn ,;
no_fromB with nArrayB ,;
KeyFieldB with B_NAME ,;
nPB2A with nPerLikeBtoA ,;
nPFB2A with nPerLikeFnameBtoA ,;
nPFA2B with nPerLikeFnameAtoB ,;
nPLB2A with nPerLikeLnameBtoA ,;
nPLA2B with nPerLikeLnameAtoB
brow nowait
ELSE
bFound = .F.
nGroupIn = nGroupIn +1
ENDIF

ENDFOR
nTimeEnd = DATETIME()
messagebox(str(nTimeEnd-nTimeStart) + &quot; sec.&quot;)
SELECT &tblResultIn
BROW
SELECT &tblResultXIn
BROW







 
I found some bugs in the code I posted above, and have fixed them. Here is the corrected code, it will also show the actual returns from soundex and metaphone (papaaui I see your code and will check it out momentarily, just wanted to get this fixed code posted)

Code:
*********************************
*!* Example of use
*********************************
LOCAL lnSoundex1, lnSoundex2, lcMetaphone1, lcMetaphone2, lcFirstWord, lcSecondWord

lnSoundex1 = SOUNDEX(&quot;FONE&quot;)
lnSoundex2 = SOUNDEX(&quot;PHONE&quot;)
lcMetaphone1 = METAPHONE(&quot;FONE&quot;)
lcMetaphone2 = METAPHONE(&quot;PHONE&quot;)
MESSAGEBOX([&quot;FONE&quot; compared to &quot;PHONE&quot;] + CHR(13)+ CHR(13) ;
	+&quot;SOUNDEX RETURNS: &quot; + CHR(13)+ CHR(9) ;
		+ TRANSFORM(lnSoundex1) + &quot; = &quot; + TRANSFORM(lnSoundex2) + &quot; (&quot; + IIF(lnSoundex1 = lnSoundex2,&quot;TRUE&quot;, &quot;FALSE&quot;) + &quot;)&quot; + CHR(13)+ CHR(13) ;
	+&quot;METAPHONE RETURNS: &quot; + CHR(13)+ CHR(9) ;
		+ lcMetaphone1 + &quot; = &quot; + lcMetaphone2 + &quot; (&quot; + IIF(lcMetaphone1 = lcMetaphone2,&quot;TRUE&quot;, &quot;FALSE&quot;) + &quot;)&quot;,0,&quot;EXAMPLE 1 OF 2&quot;)

lnSoundex1 = SOUNDEX(&quot;KREG&quot;)
lnSoundex2 = SOUNDEX(&quot;CRAIG&quot;)
lcMetaphone1 = METAPHONE(&quot;KREG&quot;)
lcMetaphone2 = METAPHONE(&quot;CRAIG&quot;)
MESSAGEBOX([&quot;KREG&quot; compared to &quot;CRAIG&quot;] + CHR(13)+ CHR(13) ;
			+&quot;SOUNDEX RETURNS: &quot; + CHR(13)+ CHR(9) ;
		 + TRANSFORM(lnSoundex1) + &quot; = &quot; + TRANSFORM(lnSoundex2) + &quot; (&quot; + IIF(lnSoundex1 = lnSoundex2,&quot;TRUE&quot;, &quot;FALSE&quot;) + &quot;)&quot; + CHR(13)+ CHR(13) ;
			+&quot;METAPHONE RETURNS: &quot; + CHR(13)+ CHR(9) ;
		 + lcMetaphone1 + &quot; = &quot; + lcMetaphone2 + &quot; (&quot; + IIF(lcMetaphone1 = lcMetaphone2,&quot;TRUE&quot;, &quot;FALSE&quot;) + &quot;)&quot;,0,&quot;EXAMPLE 2 OF 2&quot;)

DO WHILE .T.
	IF MESSAGEBOX(&quot;Would you like to try a comparison of your own?&quot;,36,&quot;GIVE IT A TRY&quot;) = 6
		lcFirstWord = ALLTRIM(INPUTBOX(&quot;Enter a name or word:&quot;, &quot;FIRST WORD TO COMPARE&quot;))
		IF EMPTY(lcFirstWord)
			MESSAGEBOX(&quot;You must enter both words.&quot;, 16, &quot;UNABLE TO RUN COMPARISON&quot;)
			LOOP
		ENDIF
		lcSecondWord =  ALLTRIM(INPUTBOX(&quot;Enter another name or word:&quot;, &quot;SECOND WORD TO COMPARE&quot;))
		IF EMPTY(lcSecondWord)
			MESSAGEBOX(&quot;You must enter both words.&quot;, 16, &quot;UNABLE TO RUN COMPARISON&quot;)
			LOOP
		ENDIF
		lnSoundex1 = SOUNDEX(lcFirstWord)
		lnSoundex2 = SOUNDEX(lcSecondWord)
		lcMetaphone1 = METAPHONE(lcFirstWord)
		lcMetaphone2 = METAPHONE(lcSecondWord)
		MESSAGEBOX([&quot;]+lcFirstWord+[&quot; compared to &quot;]+lcSecondWord+[&quot;] + CHR(13)+ CHR(13) ;
			+&quot;SOUNDEX RETURNS: &quot; + CHR(13)+ CHR(9) ;
		 		+ TRANSFORM(lnSoundex1) + &quot; = &quot; + TRANSFORM(lnSoundex2) + &quot; (&quot; + IIF(lnSoundex1 = lnSoundex2,&quot;TRUE&quot;, &quot;FALSE&quot;) + &quot;)&quot; + CHR(13)+ CHR(13) ;
			+&quot;METAPHONE RETURNS: &quot; + CHR(13)+ CHR(9) ;
				 + lcMetaphone1 + &quot; = &quot; + lcMetaphone2 + &quot; (&quot; + IIF(lcMetaphone1 = lcMetaphone2,&quot;TRUE&quot;, &quot;FALSE&quot;) + &quot;)&quot;,0,&quot;COMPARISON RESULTS&quot;)
	ELSE
		EXIT
	ENDIF
ENDDO
*********************************

*!* Original C version by Michael Kuhn <rhlab!mkuhn@uunet.uu.net>
*!* [URL unfurl="true"]http://aspell.sourceforge.net/metaphone/metaphone-kuhn.txt[/URL]
*!* Metaphone algorithm translated from C to Delphi by Tom White <wcs@lnellex.com>
*!* Translated to Visual Basic by Dave White 9/10/01
*!* Translated to Visual Foxpro by Craig Boyd (Slighthaze) 10-21-2003 craig1442@mchsi.com

*********************************
FUNCTION Metaphone (lcWord)
*********************************
#DEFINE VOWELS		&quot;AEIOU&quot;
#DEFINE FRONTV		&quot;EIY&quot;
#DEFINE VARSON		&quot;CSPTG&quot;
#DEFINE DBL			&quot;.&quot;
#DEFINE EXCPPAIR	&quot;AGKPW&quot;
#DEFINE NXTLTR		&quot;ENNNR&quot;
#DEFINE ALPHACHR	&quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&quot;
#DEFINE SIGNIFICANTCHARS	5 &&Can be set higher or lower to affect results, common to use 4 or 5
LOCAL lcB, lcC
LOCAL lcInp, lcOutp
LOCAL lnIi, lnJj
LOCAL lcCurltr, lcPrevltr, lcNextltr, lcNextltr2, lcNextltr3
LOCAL lnVowelafter, llVowelbefore, llFrontvafter, llSilent, llHard
IF PCOUNT()=0
	RETURN &quot;&quot;
ELSE
	IF TYPE(&quot;lcWord&quot;) != &quot;C&quot;
		RETURN &quot;&quot;
	ENDIF
ENDIF

lcInp = UPPER(lcWord)
lcInp = CHRTRAN(lcInp,CHRTRAN(lcInp,ALPHACHR,&quot;&quot;),&quot;&quot;) &&Remove all non-alpha characters
lcOutp = &quot;&quot;

IF LEN(lcInp) = 0
	RETURN &quot;&quot;
ENDIF

*!*--Check rules at beginning of word
IF LEN(lcInp) > 1
	lcB = SUBSTR(lcInp, 1, 1)
	lcC = SUBSTR(lcInp, 2, 1)
	lnIi = ATC(lcB, EXCPPAIR)
	lnJj = ATC(lcC, NXTLTR)
	IF lnIi = lnJj AND lnIi > 0
		lcInp = SUBSTR(lcInp, 2, LEN(lcInp) - 1)
	ENDIF
ENDIF
IF SUBSTR(lcInp, 1, 1) = &quot;X&quot;
	lcInp = STUFF(lcInp, 1, 1, &quot;S&quot;)
ENDIF
IF SUBSTR(lcInp, 1, 2) = &quot;WH&quot;
	lcInp = &quot;W&quot; + SUBSTR(lcInp, 3)
ENDIF
IF RIGHT(lcInp, 1) = &quot;S&quot;
	lcInp = LEFT(lcInp, LEN(lcInp) - 1)
ENDIF
lnIi = 0
DO WHILE (lnIi <= LEN(lcInp))
	lnIi = lnIi + 1
*!*--Main LOOP!
	llSilent = .F.
	llHard = .F.
	lcCurltr = SUBSTR(lcInp, lnIi, 1)
	llVowelbefore = .F.
	lcPrevltr = &quot; &quot;
	IF lnIi > 1
		lcPrevltr = SUBSTR(lcInp, lnIi - 1, 1)
		IF InStrC(lcPrevltr, VOWELS) > 0
			llVowelbefore = .T.
		ENDIF
	ENDIF
	IF ((lnIi = 1) AND (InStrC(lcCurltr, VOWELS) > 0))
		lcOutp = lcOutp + lcCurltr
		LOOP
	ENDIF
	llVowelafter = .F.
	llFrontvafter = .F.
	lcNextltr = &quot; &quot;
	IF lnIi < LEN(lcInp)
		lcNextltr = SUBSTR(lcInp, lnIi + 1, 1)
		IF InStrC(lcNextltr, VOWELS) > 0
			llVowelafter = .T.
		ENDIF
		IF InStrC(lcNextltr, FRONTV) > 0
			llFrontvafter = .T.
		ENDIF
	ENDIF
*!*--Skip double letters EXCEPT ones in variable double
	IF InStrC(lcCurltr, DBL) = 0
		IF lcCurltr = lcNextltr
			LOOP
		ENDIF
	ENDIF
	lcNextltr2 = &quot; &quot;
	IF LEN(lcInp) - lnIi > 1
		lcNextltr2 = SUBSTR(lcInp, lnIi + 2, 1)
	ENDIF
	lcNextltr3 = &quot; &quot;
	IF (LEN(lcInp) - lnIi) > 2
		lcNextltr3 = SUBSTR(lcInp, lnIi + 3, 1)
	ENDIF
	DO CASE
	CASE lcCurltr = &quot;B&quot;
		llSilent = .F.
		IF (lnIi = LEN(lcInp)) AND (lcPrevltr = &quot;M&quot;)
			llSilent = .T.
		ENDIF
		IF NOT (llSilent)
			lcOutp = lcOutp + lcCurltr
		ENDIF
	CASE lcCurltr = &quot;C&quot;
		IF NOT ((lnIi > 2) AND (lcPrevltr = &quot;S&quot;) AND llFrontvafter)
			IF ((lnIi > 1) AND (lcNextltr = &quot;I&quot;) AND (lcNextltr2 = &quot;A&quot;))
				lcOutp = lcOutp + &quot;X&quot;
			ELSE
				IF llFrontvafter
					lcOutp = lcOutp + &quot;S&quot;
				ELSE
					IF ((lnIi > 2) AND (lcPrevltr = &quot;S&quot;) AND (lcNextltr = &quot;H&quot;))
						lcOutp = lcOutp + &quot;K&quot;
					ELSE
						IF lcNextltr = &quot;H&quot;
							IF ((lnIi = 1) AND (InStrC(lcNextltr2, VOWELS) = 0))
								lcOutp = lcOutp + &quot;K&quot;
							ELSE
								lcOutp = lcOutp + &quot;X&quot;
							ENDIF
						ELSE
							IF lcPrevltr = &quot;C&quot;
								lcOutp = lcOutp + &quot;C&quot;
							ELSE
								lcOutp = lcOutp + &quot;K&quot;
							ENDIF
						ENDIF
					ENDIF
				ENDIF
			ENDIF
		ENDIF
	CASE lcCurltr = &quot;D&quot;
		IF ((lcNextltr = &quot;G&quot;) AND (InStrC(lcNextltr2, FRONTV) > 0))
			lcOutp = lcOutp + &quot;J&quot;
		ELSE
			lcOutp = lcOutp + &quot;T&quot;
		ENDIF
	CASE lcCurltr = &quot;G&quot;
		llSilent = .F.
		IF ((lnIi < LEN(lcInp)) AND (lcNextltr = &quot;H&quot;) AND (InStrC(lcNextltr2, VOWELS) = 0))
			llSilent = .T.
		ENDIF
		DO CASE
		CASE ((lnIi = LEN(lcInp) - 4) AND (lcNextltr = &quot;N&quot;) AND (lcNextltr2 = &quot;E&quot;) AND (lcNextltr3 = &quot;D&quot;))
			llSilent = .T.
		CASE ((lnIi = LEN(lcInp) - 2) AND (lcNextltr = &quot;N&quot;))
			llSilent = .T.
		ENDCASE
		IF (lcPrevltr = &quot;D&quot;) AND llFrontvafter
			llSilent = .T.
		ENDIF
		IF lcPrevltr = &quot;G&quot;
			llHard = .T.
		ENDIF
		IF NOT (llSilent)
			IF llFrontvafter AND (NOT (llHard))
				lcOutp = lcOutp + &quot;J&quot;
			ELSE
				lcOutp = lcOutp + &quot;K&quot;
			ENDIF
		ENDIF
	CASE lcCurltr = &quot;H&quot;
		llSilent = .F.
		IF InStrC(lcPrevltr, VARSON) > 0
			llSilent = .T.
		ENDIF
		IF llVowelbefore AND (NOT (llVowelafter))
			llSilent = .T.
		ENDIF
		IF NOT llSilent
			lcOutp = lcOutp + lcCurltr
		ENDIF
	CASE lcCurltr = &quot;F&quot; OR lcCurltr =  &quot;J&quot; OR lcCurltr =  &quot;L&quot; OR lcCurltr =  &quot;M&quot; OR lcCurltr =  &quot;N&quot; OR lcCurltr =  &quot;R&quot;
		lcOutp = lcOutp + lcCurltr
	CASE lcCurltr = &quot;K&quot; 
		IF lcPrevltr <> &quot;C&quot;
			lcOutp = lcOutp + lcCurltr
		endif
	CASE lcCurltr = &quot;P&quot;
		IF lcNextltr = &quot;H&quot;
			lcOutp = lcOutp + &quot;F&quot;
		ELSE
			lcOutp = lcOutp + &quot;P&quot;
		ENDIF
	CASE lcCurltr = &quot;Q&quot;
		lcOutp = lcOutp + &quot;K&quot;
	CASE lcCurltr = &quot;S&quot;
		IF ((lnIi > 2) AND (lcNextltr = &quot;I&quot;) AND ((lcNextltr2 = &quot;O&quot;) OR (lcNextltr2 = &quot;A&quot;)))
			lcOutp = lcOutp + &quot;X&quot;
		ENDIF
		IF (lcNextltr = &quot;H&quot;)
			lcOutp = lcOutp + &quot;X&quot;
		ELSE
			lcOutp = lcOutp + &quot;S&quot;
		ENDIF
	CASE lcCurltr = &quot;T&quot;
		IF ((lnIi > 0) AND (lcNextltr = &quot;I&quot;) AND ((lcNextltr2 = &quot;O&quot;) OR (lcNextltr2 = &quot;A&quot;)))
			lcOutp = lcOutp + &quot;X&quot;
		ENDIF
		DO CASE
		CASE lcNextltr = &quot;H&quot;
			IF ((lnIi > 1) OR (InStrC(lcNextltr2, VOWELS) > 0))
				lcOutp = lcOutp + &quot;0&quot;
			ELSE
				lcOutp = lcOutp + &quot;T&quot;
			ENDIF
		CASE NOT ((lnIi < LEN(lcInp) - 3) AND (lcNextltr = &quot;C&quot;) AND (lcNextltr2 = &quot;H&quot;))
			lcOutp = lcOutp + &quot;T&quot;
		ENDCASE
	CASE lcCurltr = &quot;V&quot;
		lcOutp = lcOutp + &quot;F&quot;
	CASE lcCurltr = &quot;W&quot; OR lcCurltr =  &quot;Y&quot;
		IF (lnIi < LEN(lcInp) - 1) AND llVowelafter
			lcOutp = lcOutp + lcCurltr
		ENDIF
	CASE lcCurltr = &quot;X&quot;
		lcOutp = lcOutp + &quot;KS&quot;
	CASE lcCurltr = &quot;Z&quot;
		lcOutp = lcOutp + &quot;S&quot;
	ENDCASE
ENDDO
RETURN LEFT(lcOutp, SIGNIFICANTCHARS)
ENDFUNC

*********************************
FUNCTION InStrC (lcSearchIn, lcSoughtCharacters)
*********************************
*!*--- Returns the position of the first character in lcSearchIn that is contained
*!*--- in the string lcSoughtCharacters.  Returns 0 if none found.
LOCAL i, lnReturn
lnReturn = 0
lcSoughtCharacters = UPPER(lcSoughtCharacters)
FOR i = 1 TO LEN(lcSearchIn)
	IF ATC(SUBSTR(lcSearchIn, i, 1), lcSoughtCharacters) > 0
		lnReturn = i
		EXIT
	ENDIF
ENDFOR
RETURN lnReturn
ENDFUNC

Slighthaze = NULL
craig1442@mchsi.com
&quot;Whom computers would destroy, they must first drive mad.&quot; - Anon​
 
Actually,I know about soundex but I don't know about
metaphone or anything else.It's sound good.I may need to learn about it.
About my code I just submit it without prepare anything. and I think it may not need to explain about detail.Let's say
ºØ­ªÑ äªÂÒÈÔÃÔ¹·Ãìâè¹ì may = ºØ­ªÑ ãªÂÒÈÔÃÔ¹·Ãìâè¹ì
ºØ­ªÑ äªÂÒÈÔÃÔ¹·Ãìâè¹ì may = ºØ¹ªÑ äªÂÒÈÔÃÔ¹·Ãìâè¹ì
ºØ­ªÑ äªÂÒÈÔÃÔ¹·Ãìâè¹ì may = ºØ¹ªÑ äªÂÈÔÃÔ¹·Ãìâè¹ì
ºØ­ªÑ äªÂÒÈÔÃÔ¹·Ãìâè¹ì may = »Ø¹ªÑ äªÂÈÔÃÔ¹·Ãìâè
.....
please check it (look like photo hunt game)
 
Thank you Dave.

Slighthaze = NULL
craig1442@mchsi.com
&quot;Whom computers would destroy, they must first drive mad.&quot; - Anon​
 
slighthaze,

Is it just me, or does your Metaphone always answer 'True' on the try-it-yourself?

Brian
 
Never mind... I didn't notice the correction :~/ This is good stuff.

Brian
 
baltman,

Yes, I thought my first implementation of it was a little too lenient. When &quot;XYZ&quot; sounds like &quot;ABC&quot; you know you've made some kind of mistake. [bigsmile] Glad you found the corrected code... only sorry I didn't get it right the first time, or at least before I posted it. Live and learn I guess.

One thing to note on the corrected code is my creation and use of the SIGNIFICANTCHARS constant. It can be used to increase or decrease the number of letters returned from the Metaphone function, kind of a way to fine tune the sensitivity/accuracy of the function.

Slighthaze = NULL
craig1442@mchsi.com
&quot;Whom computers would destroy, they must first drive mad.&quot; - Anon​
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top