计算常用字符的频率

时间:2017-01-25 05:51:07

标签: javascript arrays sorting

我如何编写一个比较两个字符串的函数,并返回一个包含两个字符串中出现的字符的对象,以及它们出现的次数?

以下是一些示例输入和输出:

"Leopard", "Lion"    // { "L": 2, "o": 2 }
"Hello",   "World"   // { "l": 3, "o": 2 }
"Flower",  "Power"   // { "o": 2, "w": 2, "e": 2, "r": 2 }
"phone",   "Memphis" // { "p": 2, "h": 2, "e": 2 }

6 个答案:

答案 0 :(得分:5)

如果您正在处理小字符串,则无需复杂化。以下方法很简单,在处理非常大的字符串时会牺牲性能。

首先,我们需要定义一个对象来保存重复字符的计数。

然后我们需要迭代每个字符串。迭代字符串有很多方法,但在下面的例子中我要么:

这两种方法都有效,因为字符串是内置可迭代对象的一个​​示例。

在迭代字符串时,我们需要首先检查重复对象中是否存在该字符。

  • 如果是,我们不需要再次检查,只需增加数字。
  • 如果没有,请扫描另一个字符串以查看该字符是否出现在那里。
    • 如果是,请递增该字符的重复计数器。
    • 如果没有,请不要做任何事。

使用此方法,您不会计算不重复的字符,也不会尝试验证已经验证过的字符。这可以通过更长的字符串提供一些显着的速度提升。

完成两个字符串的迭代后,只需返回重复的持有者对象。

ES6示例

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")            // { "L": 2, "o": 2 }
    ("Hello", "World")             // { "l": 3, "o": 2 }
    ("Flower", "Power")            // { "o": 2, "w": 2, "e": 2, "r": 2 }
    ("phone", "Memphis")           // { "p": 2, "h": 2, "e": 2 }
    ("Bookkeeper", "Zookeeper")    // { "o": 4, "k": 3, "e": 6, "p": 2, "r": 2 }
    ("Some thing", "Other thing"); // { "e": 2, " ": 2, "t": 3, "h": 3, "i": 2, "n": 2, "g": 2 }

ES5示例

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")            // { "L": 2, "o": 2 }
    ("Hello", "World")             // { "l": 3, "o": 2 }
    ("Flower", "Power")            // { "o": 2, "w": 2, "e": 2, "r": 2 }
    ("phone", "Memphis")           // { "p": 2, "h": 2, "e": 2 }
    ("Bookkeeper", "Zookeeper")    // { "o": 4, "k": 3, "e": 6, "p": 2, "r": 2 }
    ("Some thing", "Other thing"); // { "e": 2, " ": 2, "t": 3, "h": 3, "i": 2, "n": 2, "g": 2 }

当您开始使用非常大的字符串时,性能会成为一个问题。以下方法使用一些算法魔法来获得性能。

首先,找到两个字符串中每个字母的频率。 Working with a sorted array is faster than working with an unsorted array,并节省一些时间,因为我们可以将字符串拆分为常见字符组,而不是计算每个字符。

我使用Map对象在下一个函数中利用Map#has方法 - 比Array#indexOf快得多。

// Calculate the frequency of each character in a string
const calculateCharacterFrequency = string => {
   // Split the string into individual characters
    const array = [...string];
    // Sort the split string
    const sorted = array.sort();
    // Join the split string
    const joined = sorted.join("");
    // Split the string into groups of common characters
    const matches = joined.match(/(.)\1*/g);
    // Iterate the groups
    const frequency = matches.reduce(
        (frequency, character) => { 
            // Insert char and frequency into map
            frequency.set(character[0], character.length);
            // return frequency map for use in next iteration
            return frequency;
        },
        // This is the map that will be passed as `frequency`
        new Map()
    );
    // Return the map
    return frequency;
};

接下来,找到每个字符串之间的常用字符,并创建一个包含每个常用字符频率的映射。

这里最大的性能提升是创建一个独特的字符集中出现的所有字符集。我利用Set对象的独特性来从数组中删除重复项,还有其他方法可以从数组中删除重复项,但这是我测试过的最快的重复项。这样,我们只迭代一个unqiue集并检查字符是否出现在两个映射中。

// Calculate the frequency of each character two strings have in common
const calculateCommonCharacterFrequency = (v1, v2) => {
    // Get frequency map of first string
    v1 = calculateCharacterFrequency(v1);
    // Get frequency map of second string
    v2 = calculateCharacterFrequency(v2);
    // Create an array containing a list of all characters occuring in either string
    const combinedCharacterArray = [...v1.keys(), ...v2.keys()];
    // Remove duplicate characters
    const combinedUniqueCharacterSet = new Set(combinedCharacters);
    // Convert set back to array
    const combinedUniqueCharacterArray = Array.from(combinedUniqueSet);
    // Iterate the unique array of common characters
    const commonFrequency = combinedUniqueCharacterArray.reduce(
        (commonFrequency, character) => {
            // Check if both strings have the character
            const isCommon = v1.has(character) && v2.has(character);
            if(isCommon) {
                // Find the sum of the frequency of the character in both strings
                const combinedFrequency = v1.get(character) + v2.get(character);
                // Insert character and frequency into map
                commonFrequency.set(character, combinedFrequency);
            }
            // return frequency map for use in next iteration
            return commonFrequency;
        }, 
        // This is the map that will be passed as `commonFrequency`
        new Map()
    );
    // Return the map
    return commonFrequency;
};

简明示例

为了便于阅读和解释,上面的例子已经扩展。以下示例已经过浓缩以节省空间。

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")            // { "L": 2, "o": 2 }
    ("Hello", "World")             // { "l": 3, "o": 2 }
    ("Flower", "Power")            // { "o": 2, "w": 2, "e": 2, "r": 2 }
    ("phone", "Memphis")           // { "p": 2, "h": 2, "e": 2 }
    ("Bookkeeper", "Zookeeper")    // { "o": 4, "k": 3, "e": 6, "p": 2, "r": 2 }
    ("Some thing", "Other thing"); // { "e": 2, " ": 2, "t": 3, "h": 3, "i": 2, "n": 2, "g": 2 }

答案 1 :(得分:1)

我将从一个名为if($cek_user->num_rows()>0){ $row = $cek_user->row(); $cek = $row ->hak_akses; if($cek=='admin'){ $ambil = $this->db->query("SELECT table_name FROM information_schema.tables WHERE table_schema='public' and table_name != 'tablelogin'"); return $ambil->result(); } else if($cek=='semi_admin'){ $ambil = $this->db->query("SELECT table_name FROM information_schema.tables WHERE table_schema='public' and table_name != 'tablelogin' and table_name != 'Film' and table_name != 'Hewan'"); return $ambil->result(); } else if($cek == 'user'){ $ambil = $this->db->query("SELECT table_name FROM information_schema.tables WHERE table_schema='public' and table_name != 'tablelogin' and table_name != 'Gudang' and table_name != 'inventaris' and table_name != 'Pegawai'"); return $ambil->result(); } } else { $hasil = null; } 的简单函数开始,计算单个字符串中字符的出现次数。您可以在其他地方找到这个,但我提供了一个简单的版本。输出是一个按字符键入的对象,计数为值。

然后我们可以解决在两个字符串中找到匹配字符的问题,作为两个对象的合并,我们在一个名为countChars的函数中实现。



combineObjectsByKey




替代方法

你也可以简单地连接字符串,计算连接字符串中的字符,然后删除两个字符串中没有出现的字符:

function countChars(str) {
  return str.split('').reduce((result, chr) => {
    if (!(chr in result)) result[chr] = 0;
    result[chr]++;
    return result;
  }, {});
}

function combineObjectsByKey(o1, o2, fn) {
  const result = {};

  Object.keys(o1).forEach(key => {
    if (key in o2) result[key] = fn(o1[key], o2[key]);
  });

  return result;
}

function add(a, b) { return a + b; }

function matchingCounts(str1, str2) {
  return combineObjectsByKey(countChars(str1), countChars(str2), add);
}

console.log(matchingCounts("Leopard", "Lion"));

答案 2 :(得分:0)

这是一个应该是FAST的选项(主要是因为它根本没有做任何花哨的事情)。这将解释字符大小写敏感,所有小写字母a-z字符,所有大写字母A-Z字符,数字0-9字符和空字符字符。

这里我只使用并行数组作为第一个和第二个字的字符的ASCII字符值。

 xml = XML.toString(invoiceDetailObj);

以下是此代码的jsFiddle,可供使用和测试。

答案 3 :(得分:0)

虽然这个例子可能更优雅,但我认为这种方法更简单,更具可读性。

这是一个Pen来玩。

function getRepeated(x, y) {
  let result = [];
  x.split('').forEach(value => {
    let array = testCharacterToString(value, y);
    if (array.length > 0) {
      result.push(array[0]);
    }
  });
  y.split('').forEach(value => {
    let array = testCharacterToString(value, x);
    if (array.length > 0) {
      result.push(array[0]);
    }
  });

  result = result.reduce(function(prev, cur) {
    prev[cur] = (prev[cur] || 0) + 1;
    return prev;
  }, {});

  return JSON.stringify(result) // {"o":4,"k":3,"e":6,"p":2,"r":2}
}

function testCharacterToString(char, string) {
  return string.split('').filter(value => {
    return value === char;
  });
}

var test = function(arg1,arg2){ return (console.log(getRepeated(arg1, arg2)),test) };

test("Leopard", "Lion")            // { "L": 2, "o": 2 }
    ("Hello", "World")             // { "l": 3, "o": 2 }
    ("Flower", "Power")            // { "o": 2, "w": 2, "e": 2, "r": 2 }
    ("phone", "Memphis")           // { "p": 2, "h": 2, "e": 2 }
    ("Bookkeeper", "Zookeeper")    // { "o": 4, "k": 3, "e": 6, "p": 2, "r": 2 }
    ("Some thing", "Other thing"); // { "e": 2, " ": 2, "t": 3, "h": 3, "i": 2, "n": 2, "g": 2 }

答案 4 :(得分:0)

这是一个非常好的问题。如果我做对了,你可以很容易地在O(2n)中做如下;



function relationIndex(s){
  var a = s.split(/\s+/),
   hash = a[0].length < a[1].length ? Array.prototype.reduce.call(a[1], (p,c) => (p[c] && (p[c] = Math.abs(p[c])+1),p), Array.prototype.reduce.call(a[0], (p,c) => (p[c] = --p[c] || -1,p),{}))
                                    : Array.prototype.reduce.call(a[0], (p,c) => (p[c] && (p[c] = Math.abs(p[c])+1),p), Array.prototype.reduce.call(a[1], (p,c) => (p[c] = --p[c] || -1,p),{}));
  for (var k in hash) hash[k] <= 0 && delete hash[k];
  return hash;
}

var str = "Lion Leopard";
console.log(relationIndex(str));
str = "Brontosaurus Butterfly";
console.log(relationIndex(str));
str = "London Paris";
console.log(relationIndex(str));
str = "phone Memphis";
console.log(relationIndex(str));
&#13;
&#13;
&#13;

答案 5 :(得分:-1)

这是一个更易读的版本的缩略图...

function count(v1, v2) {
  let map = {};
  Array.prototype.forEach.call(v1, char => map[char] = map[char]++ || 1);
  Array.prototype.forEach.call(v2, char => {
    if(map[char]) map[char] += 1; 
  });
  for(let char in map) {
    if(map[char] < 2) {
      delete map[char];
    }
  }
  console.log(map)
}
count('Lion', 'Leopard');