Select the types of activity you want to include in your feed.
fix: Fix reorder variant string length issues
Depending on the order strings were given the recombined, ordered strings could have null/undefined in them due to bad logic. Refactor entire approach to be parameter order invariant.
···1313 * Useful when only the differences in content are important, but not the order of the content
1414 * */
1515 reorder?: boolean
1616+1717+ /**
1818+ * When `reorder` is used this determines how to split each string into the tokens that will be reordered.
1919+ *
2020+ * The value of this property is used in String.split() -- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split#separator
2121+ *
2222+ * @default " "
2323+ * */
2424+ delimiter?: string | RegExp
1625}
17261827export interface StringSamenessResult {
+67-23
src/index.ts
···2626 transforms = strDefaultTransforms,
2727 strategies = defaultStrategies,
2828 reorder = false,
2929+ delimiter = ' '
2930 } = options || {};
30313131- const cleanA = transforms.reduce((acc, curr) => curr(acc), valA);
3232+ let cleanA = transforms.reduce((acc, curr) => curr(acc), valA);
3233 let cleanB = transforms.reduce((acc, curr) => curr(acc), valB);
33343434- const shortest = cleanA.length > cleanB.length ? cleanB : cleanA;
3535-3635 if (reorder) {
3736 // we want to ignore order of tokens as much as possible (user does not care about differences in word order, just absolute differences in characters overall)
3838- // so we will reorder cleanB so its tokens match the order or tokens in cleanA as closely as possible
3737+ // so we will reorder the shorter of the two strings so its tokens match the order of tokens in the longer string as closely as possible
3938 // before we run strategies
4040- cleanB = reorderStr(cleanA, cleanB);
3939+ const [orderedX, orderedY] = reorderStr(cleanA, cleanB);
4040+ cleanA = orderedX;
4141+ cleanB = orderedY;
4142 }
4343+4444+ const shortest = cleanA.length > cleanB.length ? cleanB : cleanA;
42454346 const stratResults: NamedComparisonStrategyObjectResult[] = [];
4447···7679 }
7780}
78817979-export const reorderStr = (cleanA: string, cleanB: string, options?: StringComparisonOptions): string => {
8080- // to do the reordering we will use stringSameness with the provided strats to match against each token in cleanA and choose the closest token in cleanB
8181- // and add the end concat any remaining tokens from cleanB to the reordered string
8282- const aTokens = cleanA.split(' ');
8383- const bTokens = cleanB.split(' ');
8484- const orderedCandidateTokens = aTokens.reduce((acc: { ordered: string[], remaining: string[] }, curr) => {
8282+export const reorderStr = (strA: string, strB: string, options?: StringComparisonOptions): [string, string] => {
8383+8484+ const {
8585+ transforms = strDefaultTransforms,
8686+ strategies = defaultStrategies,
8787+ delimiter = ' '
8888+ } = options || {};
8989+9090+ const cleanA = transforms.reduce((acc, curr) => curr(acc), strA);
9191+ const cleanB = transforms.reduce((acc, curr) => curr(acc), strB);
9292+9393+ // split by "token"
9494+ const eTokens = cleanA.split(delimiter);
9595+ const cTokens = cleanB.split(delimiter);
9696+9797+9898+ let longerTokens: string[],
9999+ shorterTokens: string[];
100100+101101+ if (eTokens.length > cTokens.length) {
102102+ longerTokens = eTokens;
103103+ shorterTokens = cTokens;
104104+ } else {
105105+ longerTokens = cTokens;
106106+ shorterTokens = eTokens;
107107+ }
108108+109109+ // we will use longest string (token list) as the reducer and order the shorter list to match it
110110+ // so we don't have to deal with undefined positions in the shorter list
111111+112112+ const orderedCandidateTokens = longerTokens.reduce((acc: { ordered: string[], remaining: string[] }, curr) => {
113113+ // if we've run out of tokens in the shorter list just return
114114+ if (acc.remaining.length === 0) {
115115+ return acc;
116116+ }
117117+118118+ // on each iteration of tokens in the long list
119119+ // we iterate through remaining tokens from the shorter list and find the token with the most sameness
120120+85121 let highScore = 0;
8686- let highIndex: undefined | number = 0;
122122+ let highIndex = 0;
87123 let index = 0;
88124 for (const token of acc.remaining) {
8989- const result = stringSameness(curr, token, {...options, reorder: false});
9090- if (result.highScore > highScore) {
9191- highScore = result.highScore;
125125+ const result = stringSameness(curr, token, {strategies});
126126+ if (result.highScoreWeighted > highScore) {
127127+ highScore = result.highScoreWeighted;
92128 highIndex = index;
93129 }
94130 index++;
95131 }
96132133133+ // then remove the most same token from the remaining short list tokens
97134 const splicedRemaining = [...acc.remaining];
9898- if(highIndex <= splicedRemaining.length - 1) {
9999- splicedRemaining.splice(highIndex, 1);
100100- }
101101- const ordered = highIndex <= acc.remaining.length - 1 ? acc.ordered.concat(acc.remaining[highIndex]) : acc.ordered;
135135+ splicedRemaining.splice(highIndex, 1);
102136103103- return {ordered: ordered, remaining: splicedRemaining};
104104- }, {ordered: [], remaining: bTokens});
105105- const allOrderedCandidateTokens = orderedCandidateTokens.ordered.concat(orderedCandidateTokens.remaining);
106106- return allOrderedCandidateTokens.join(' ');
137137+ return {
138138+ // finally add the most same token to the ordered short list
139139+ ordered: acc.ordered.concat(acc.remaining[highIndex]),
140140+ // and return the remaining short list tokens
141141+ remaining: splicedRemaining
142142+ };
143143+ }, {
144144+ // "ordered" is the result of ordering tokens in the shorter list to match longer token order
145145+ ordered: [],
146146+ // remaining is the initial shorter list
147147+ remaining: shorterTokens
148148+ });
149149+150150+ return [longerTokens.join(' '), orderedCandidateTokens.ordered.join(' ')];
107151}
108152109153const createStringSameness = (defaults: StringComparisonOptions) => {
+9-4
tests/index.test.ts
···103103 ]
104104105105 for(const test of exact) {
106106- assert.equal(reorderStr(test[0], test[1]), test[0])
106106+ const res = reorderStr(test[0], test[1]);
107107+ assert.equal(res[1], test[1])
107108 }
108109 });
109110···111112112113 const a = 'this is correct order';
113114 const b = 'order correct this is and extra';
114114- const expected = 'this is correct order and extra'
115115+ const expected = 'order correct this is'
116116+117117+ const res = reorderStr(a, b);
115118116116- assert.equal(reorderStr(a, b), expected);
119119+ assert.equal(res[1], expected);
117120118121 const aLong = 'this vorrect order with more tokens';
119122 const bLong = 'order correct this extra';
120123 const expectedLong = 'this correct order extra';
121124122122- assert.equal(reorderStr(aLong, bLong), expectedLong);
125125+ const res2 = reorderStr(aLong, bLong);
126126+127127+ assert.equal(res2[1], expectedLong);
123128124129 });
125130