JS:将布尔列表转换为紧凑字符串?

The*_*ler 2 javascript

我在 javascript 中有一个巨大的布尔值列表,并希望将它们作为参数编码在 url 中,但又不占用太多空间。所以我在想,是否可以将布尔数组转换为位数组,然后将这些位转换为字符串?

例如,如果我的布尔值列表是:

[false, true, true, false, false, false, false, true]
Run Code Online (Sandbox Code Playgroud)

然后以位为单位

[0, 1, 1, 0, 0, 0, 0, 1]
Run Code Online (Sandbox Code Playgroud)

这是仅字母的二进制文件a(至少根据this)。

这样的事情可能吗?如果是这样,如何转换回来?

Aar*_*zyk 5

您可以使用地图:

console.log( [false, true, true].map(item => item ? 1 : 0).join("") );
Run Code Online (Sandbox Code Playgroud)

但是地图在 Internet Explorer 中不能很好地工作。相反,我会使用一个简单的 for 循环:

var bools = [false, true, true];
for(var i = 0; i < bools.length; i++) bools[i] = bools[i] ? 1 : 0;
console.log(bools.join(""));
Run Code Online (Sandbox Code Playgroud)

但是如果你能让字符串比 0 和 1 更短,那就太酷了。如果您可以将相同值的多个连续布尔值缩小为一个字符会怎样?那么[true, true, true, true]只是"4"代替"1111"? 这就是我在创建此代码时采用并运行的想法:

var trueMultiples = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'A', 'S', 'D'];
var falseMultiples = ['0', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'm', 'n', 'b', 'p', 'x', 'c', 'v', 'F', 'G', 'H', 'J', 'K', 'L', 'Z', 'X', 'C', 'V', 'B', 'N', 'M'];

function encryptBools(bools) {
  var str = "",
    run = [];

  for (var i = 0; i < bools.length; i++) {
    if (run.length == 0 || run[run.length - 1] === bools[i]) {
      //stack up successive trues or successive falses as a "run"
      run.push(bools[i]);
    } else {
      //when the run ends, convert it to a trueMultiples or falseMultiples character
      var encryptionSet = bools[i] ? falseMultiples : trueMultiples;
      while (run.length > encryptionSet.length) {
        //if it's too long to be a single character, use multiple characters
        str += encryptionSet[encryptionSet.length - 1];
        run = run.slice(0, run.length - encryptionSet.length);
      }
      str += encryptionSet[run.length - 1];
      run = [bools[i]];
    }
  }

  if (bools.length > 0) {
    //for the last run, convert it to a trueMultiples or falseMultiples character
    var encryptionSet = run[run.length - 1] ? trueMultiples : falseMultiples;
    while (run.length > encryptionSet.length) {
      //if it's too long to be a single character, use multiple characters
      str += encryptionSet[encryptionSet.length - 1];
      run = run.slice(0, run.length - encryptionSet.length);
    }
    str += encryptionSet[run.length - 1];
  }

  return str;
}

function decryptBools(str) {
  var bools = [];

  for (var i = 0; i < str.length; i++) {
    if (trueMultiples.indexOf(str[i]) > -1) {
      for (var j = 0; j <= trueMultiples.indexOf(str[i]); j++) {
        bools.push(true);
      }
    } else if (falseMultiples.indexOf(str[i]) > -1) {
      for (var j = 0; j <= falseMultiples.indexOf(str[i]); j++) {
        bools.push(false);
      }
    }
  }

  return bools;
}

var bools = [true, false, false, false, false, false, true, true, true, true, false];
console.log("ORIGINAL:" + JSON.stringify(bools));

var encryptedBools = encryptBools(bools);
console.log("ENCRYPTED: " + encryptedBools);

var decryptedBools = decryptBools(encryptedBools);
console.log("DECRYPTED: " + JSON.stringify(decryptedBools));
Run Code Online (Sandbox Code Playgroud)

trueMultiplesfalseMultiples是表示您拥有该值的连续布尔值的数量的字符。例如,"3"表示连续 3 次为真,而"s"表示连续 3 次为假。

最好的情况是,您可以将 200 个布尔值减少到7 个字符的长字符串。最坏的情况,200 个字符长。预期长度为 100.497 个字符。

我坚持使用基本的字母数字字符,但如果您愿意,可以随意添加“-”、“_”和“~”。它们对 urls安全的

更新

实际上,让我震惊的是,我们将布尔值转换为 0 和 1 的第一步给我们留下了如下所示的内容:

[1, 1, 0, 1]
Run Code Online (Sandbox Code Playgroud)

对我来说,这看起来与二进制数惊人地相似。如果我们将该数组连接在一起并获取1101然后将其切换为十进制表示法以将其显示为13呢?或者更好的是,我们可以使用更高的基数,比如 36 来让它读成d! 能够像这样切换数字的基数是产生较小结果的绝佳方式!

现在,我知道你在想什么。如果false开头是's 而数字最终是类似的东西001怎么办?领先0的会迷路!!嗯,别担心。我们可以将我们的算法设置为始终1在切换基数之前在开头添加 a 。这样,所有的0's 都将保持重要。

这里有一些限制。有 200 多个布尔值,这些人为的数字将是巨大的。事实上,对于 JavaScript 来说太大了。我们需要将其分解为可管理的块,然后将这些块连接在一起以获得我们的结果。

旁注:我们可以投入更多的工作来表示有多少个前导零而不是强制前导 1 来改善我们的最佳情况,但我认为这实际上可能会损害我们的平均情况,所以我没有'不。强制前导 1 会强制我们所有的完整块始终为11字符长,这一事实使我们无需额外的分隔符。为什么要搞这个?

无论如何,这就是我最终得到的:

它将采用一个包含 200 个布尔值的数组,并将其始终缩减为 42 个字符的字符串。

这很好,但您可能会问自己为什么我们只使用 base 36?我们可以走得更高吗?答案是我选择了 36,因为它已经是 JavaScript 的parseInt函数中内置的最高数字。如果我们愿意添加自定义基本转换代码,我们可以走得更高。这里有一个很好的答案它提供了一个很好的基础转换函数,所以我将复制它们的函数并将其粘贴到这里来证明我的观点:

function compress(bools) {
  var sections = [], MAX_SAFE_SECTION = 52;
  for (var i = 0; i < bools.length; i++) {
    if (i % MAX_SAFE_SECTION == 0) sections.push([]);
    sections[Math.floor(i / MAX_SAFE_SECTION)][i % MAX_SAFE_SECTION] = bools[i] ? 1 : 0;
  }
  for (var i = 0; i < sections.length; i++) sections[i] = parseInt("1" + sections[i].join(""), 2).toString(36);
  return sections.join("");
}

function expand(str) {
  var sections = [];
  while (str.length > 0) str = str.replace(sections[sections.length] = str.substring(0, 11), "");
  for (var i = 0; i < sections.length; i++) sections[i] = parseInt(sections[i], 36).toString(2).substring(1);
  var bools = sections.join("").split("");
  for (var i = 0; i < bools.length; i++) bools[i] = bools[i] == "1";
  return bools;
}


var bools = [true, false, false, false, false, false, true, true, true, true, false];
console.log("ORIGINAL:" + JSON.stringify(bools));

var compressedBools = compress(bools);
console.log("COMPRESSED: " + compressedBools);

var expandedBools = expand(compressedBools);
console.log("EXPANDED: " + JSON.stringify(expandedBools));
Run Code Online (Sandbox Code Playgroud)

我们可以使用这个自定义函数安全地达到 base 62。这意味着我们可以采用一个包含 200 个布尔值的数组,并将其一致地缩减为 35 个字符的字符串。如果您的数组中没有大量顺序重复,您可能希望改用此选项。这是我选择的算法。

  • 很好的完整答案。作为旁注,这不是“加密”,而是“压缩” (2认同)