我在 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)。
这样的事情可能吗?如果是这样,如何转换回来?
您可以使用地图:
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)
trueMultiples和falseMultiples是表示您拥有该值的连续布尔值的数量的字符。例如,"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字符长,这一事实使我们无需额外的分隔符。为什么要搞这个?
无论如何,这就是我最终得到的:
[1, 1, 0, 1]
Run Code Online (Sandbox Code Playgroud)
它将采用一个包含 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 个字符的字符串。如果您的数组中没有大量顺序重复,您可能希望改用此选项。这是我选择的算法。