If you work with small lines, there is no need to complicate the situation. The following method is simple, due to the performance when working with very large strings.
First we need to define an object for storing counters for our repeating characters.
Then we need to iterate each row. There are many ways to iterate over a string, but in the following examples, I also:
Both of these methods work because the string is an example of an inline iterative object.
During line iteration, we need to first check to see if the character exists in the repeating object.
- If so, we do not need to check it again, just increase the number.
- If it is not, look at another line to see if the character exists there.
- If so, increase the repeating counter for this character.
- If it is not, do nothing.
Using this method, you do not count characters that are not duplicates, and you are not trying to verify characters that you have already checked. This should provide some significant speed improvements with longer strings.
Once you finish iterating over both lines, just return the duplicate holder object.
ES6 example
const countLetters = (v1, v2) => { const out = {}; for(let char of v1) (out[char] || v2.indexOf(char) !== -1) && (out[char] = (out[char] || 0) + 1); for(let char of v2) (out[char] || v1.indexOf(char) !== -1) && (out[char] = (out[char] || 0) + 1); return out; } const test = (v1,v2) => (console.log(JSON.stringify(countLetters(v1, v2))),test); test("Leopard", "Lion")
ES5 example
var countLetters = function(v1, v2) { var out = {}; Array.prototype.forEach.call(v1, function(char) { if(out[char] || v2.indexOf(char) !== -1) out[char] = (out[char] || 0) + 1; }); Array.prototype.forEach.call(v2, function(char) { if(out[char] || v1.indexOf(char) !== -1) out[char] = (out[char] || 0) + 1; }); return out; } var test = function(v1,v2){ return (console.log(JSON.stringify(countLetters(v1, v2))),test) }; test("Leopard", "Lion")
Performance becomes a problem when you start working with very large lines. The following method uses some algorithmic magic to improve performance.
First find the frequency of each letter in both lines. Working with a sorted array is faster than working with an unsorted array , and saves time, because we can divide the string into groups of common characters, rather than counting each character.
I use the Map
object to use the Map#has
method, which is much faster than Array#indexOf
in the following function.
Then find the common characters between each line and create a map containing the frequency of each common character.
The biggest performance increase here is to create a unique set of all characters that appear on any line. I use the unique nature of the Set
object to remove duplicates from an array, there are other ways to remove duplicates from an array, but this was the fastest I tested. Thus, we execute only one set of unqiue and verify that the symbol is found on both cards.
Condensed Example
The above example has been extended for readability and explanation. The following example has been compressed to save space.
const calcCharFreq = string => [...string].sort().join("").match(/(.)\1*/g).reduce((freq, char) => (freq.set(char[0], char.length), freq), new Map()); const calcCommonCharFreq = (v1, v2) => { v1 = calcCharFreq(v1); v2 = calcCharFreq(v2); return Array.from(new Set([...v1.keys(), ...v2.keys()])).reduce((dup, char) => ((v1.has(char) && v2.has(char)) && dup.set(char, v1.get(char) + v2.get(char)), dup), new Map()); }; const test = (v1,v2) => (console.log('{ '+Array.from(calcCommonCharFreq(v1, v2)).reduce((pairs, value) => (pairs.push('"'+value[0]+'": '+value[1]), pairs), []).join(", ")+' }'), test); test("Leopard", "Lion")