如何仅从数组的左侧和右侧修剪连续的虚假成员?
我想编写一个类似这样的方法
getLookupKey('prefix', 'prefix', 'middle', 'suffix', 'suffix');
// => 'prefix.prefix.middle.suffix.suffix'
getLookupKey(null, 'prefix', 'middle', 'suffix', null);
// => 'prefix.middle.suffix'
getLookupKey('name');
// => 'name'
getLookupKey();
// => ''
getLookupKey('prefix', null, 'suffix');
// => throws error
其中getLookupKey
获取可变数量的参数并生成查找键。
我希望这个方法忽略数组左边和右边的空值,但如果中间有任何空值则抛出。
以下是一些行为规则:
答案 0 :(得分:1)
不使用任何库,只使用纯Javascript,因此拥有的代码行数多于库的含糖语法。应该具有比使用多个循环更好的性能,因为它只是一个循环,使用更多的参数会更加明显。
通过查看类似数组的arguments
对象的每一端来工作,标记元素未评估为start
的{{1}}和end
位置。标记后,其他元素将连接到false
字符串。如果我们有内容,那么我们会连接result
和start
元素(如果存在)以提供完整内容,最后end
return
。
result

function getLookupKey() {
var length = arguments.length,
result = '',
index,
start,
end,
stop,
value;
for (index = 0; index < length; index += 1) {
if (arguments[index]) {
start = index;
break;
}
}
for (index = length; index > start; index -= 1) {
if (arguments[index]) {
end = index;
break;
}
}
stop = end - 1;
for (index = start + 1; index < end; index += 1) {
value = arguments[index];
if (!value) {
throw new SyntaxError('middle evaluates false');
}
result += value;
if (index !== stop) {
result += '.';
}
}
if (typeof start === 'number') {
if (result) {
result = '.' + result;
}
result = arguments[start] + result;
}
if (typeof end === 'number') {
result += '.' + arguments[end];
}
return result;
}
function log(str) {
document.getElementById('out').textContent += str + '\n';
}
var tests = [
[
[null, 'middle'], 'middle'
],
[
[null, null, null, 'middle', null, null], 'middle'
],
[
[null, null, 'middle', null, null, null], 'middle'
],
[
['prefix1', 'prefix2', 'middle', 'suffix1', 'suffix2'], 'prefix1.prefix2.middle.suffix1.suffix2'
],
[
[null, 'prefix', 'middle', 'suffix', null], 'prefix.middle.suffix'
],
[
['name'], 'name'
],
[
[null], ''
],
[
[null, null, null, null], ''
],
[
['a', null, null, null], 'a'
],
[
[null, null, null, 'b'], 'b'
],
[
[], ''
],
[
['prefix', null, 'suffix'], Error
],
[
[null, null, 'a', 'b', 'c', null, null], 'a.b.c'
],
[
['a', null, 'b'], Error
],
[
[null, 'middle', null], 'middle'
],
[
[null, 'middle1', null, 'middle2', 'middle3', null], Error
],
[
[null, 'middle1', 'middle2', 'middle3', null, 'middle4', 'middle5', null], Error
],
[
[null, 'middle1', null, null, null, null, 'middle2', null], Error
]
];
function test(fn) {
var length = tests.length,
index,
expected,
args,
actual;
for (index = 0; index < length; index += 1) {
args = tests[index][0];
expected = tests[index][1];
if (typeof expected === 'string') {
try {
actual = fn.apply(null, args);
} catch (e) {
actual = e.message;
}
log('Test ' + index + ': Expected: "' + expected + '" Actual: "' + actual + '"');
} else if (expected === Error) {
expected = 'middle evaluates false';
try {
actual = fn.apply(null, args);
} catch (e) {
actual = e.message;
}
log('Test ' + index + ': Expected: "' + expected + '" Actual: "' + actual + '"');
} else {
log('Test ' + index + ': coder error');
}
}
}
test(getLookupKey);
&#13;
简单但效率低于上述,在ES5中
<pre id="out"></pre>
&#13;
function getLookupKey() {
var arr = Array.prototype.reduce.call(arguments, function (acc, arg) {
if (arg || acc.length) {
acc.push(arg);
}
return acc;
}, []).reduceRight(function (acc, arg) {
if (arg || acc.length) {
acc.unshift(arg);
}
return acc;
}, []);
if (!arr.every(Boolean)) {
throw new SyntaxError('middle evaluates false');
}
return arr.join('.');
}
function log(str) {
document.getElementById('out').textContent += str + '\n';
}
var tests = [
[
[null, 'middle'], 'middle'
],
[
[null, null, null, 'middle', null, null], 'middle'
],
[
[null, null, 'middle', null, null, null], 'middle'
],
[
['prefix1', 'prefix2', 'middle', 'suffix1', 'suffix2'], 'prefix1.prefix2.middle.suffix1.suffix2'
],
[
[null, 'prefix', 'middle', 'suffix', null], 'prefix.middle.suffix'
],
[
['name'], 'name'
],
[
[null], ''
],
[
[null, null, null, null], ''
],
[
['a', null, null, null], 'a'
],
[
[null, null, null, 'b'], 'b'
],
[
[], ''
],
[
['prefix', null, 'suffix'], Error
],
[
[null, null, 'a', 'b', 'c', null, null], 'a.b.c'
],
[
['a', null, 'b'], Error
],
[
[null, 'middle', null], 'middle'
],
[
[null, 'middle1', null, 'middle2', 'middle3', null], Error
],
[
[null, 'middle1', 'middle2', 'middle3', null, 'middle4', 'middle5', null], Error
],
[
[null, 'middle1', null, null, null, null, 'middle2', null], Error
]
];
function test(fn) {
var length = tests.length,
index,
expected,
args,
actual;
for (index = 0; index < length; index += 1) {
args = tests[index][0];
expected = tests[index][1];
if (typeof expected === 'string') {
try {
actual = fn.apply(null, args);
} catch (e) {
actual = e.message;
}
log('Test ' + index + ': Expected: "' + expected + '" Actual: "' + actual + '"');
} else if (expected === Error) {
expected = 'middle evaluates false';
try {
actual = fn.apply(null, args);
} catch (e) {
actual = e.message;
}
log('Test ' + index + ': Expected: "' + expected + '" Actual: "' + actual + '"');
} else {
log('Test ' + index + ': coder error');
}
}
}
test(getLookupKey);
&#13;
简单但效率较低,在ES6中
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.1.7/es5-shim.min.js"></script>
<pre id="out"></pre>
&#13;
function fVal(test, alt) {
return test !== -1 ? test : alt;
}
function getLookupKey() {
var arr = Array.prototype.slice.call(arguments),
length = arr.length,
begin = fVal(arr.findIndex(Boolean), length),
end = length - fVal(arr.slice().reverse().findIndex(Boolean), 0);
arr = arr.slice(begin, end);
if (!arr.every(Boolean)) {
throw new SyntaxError('middle evaluates false');
}
return arr.join('.');
}
function log(str) {
document.getElementById('out').textContent += str + '\n';
}
var tests = [
[
[null, 'middle'], 'middle'],
[
[null, null, null, 'middle', null, null], 'middle'],
[
[null, null, 'middle', null, null, null], 'middle'],
[
['prefix1', 'prefix2', 'middle', 'suffix1', 'suffix2'], 'prefix1.prefix2.middle.suffix1.suffix2'],
[
[null, 'prefix', 'middle', 'suffix', null], 'prefix.middle.suffix'],
[
['name'], 'name'],
[
[null], ''],
[
[null, null, null, null], ''],
[
['a', null, null, null], 'a'],
[
[null, null, null, 'b'], 'b'],
[
[], ''],
[
['prefix', null, 'suffix'], Error],
[
[null, null, 'a', 'b', 'c', null, null], 'a.b.c'],
[
['a', null, 'b'], Error],
[
[null, 'middle', null], 'middle'],
[
[null, 'middle1', null, 'middle2', 'middle3', null], Error],
[
[null, 'middle1', 'middle2', 'middle3', null, 'middle4', 'middle5', null], Error],
[
[null, 'middle1', null, null, null, null, 'middle2', null], Error]
];
function test(fn) {
var length = tests.length,
index,
expected,
args,
actual;
for (index = 0; index < length; index += 1) {
args = tests[index][0];
expected = tests[index][1];
if (typeof expected === 'string') {
try {
actual = fn.apply(null, args);
} catch (e) {
actual = e.message;
}
log('Test ' + index + ': Expected: "' + expected + '" Actual: "' + actual + '"');
} else if (expected === Error) {
expected = 'middle evaluates false';
try {
actual = fn.apply(null, args);
} catch (e) {
actual = e.message;
}
log('Test ' + index + ': Expected: "' + expected + '" Actual: "' + actual + '"');
} else {
log('Test ' + index + ': coder error');
}
}
}
test(getLookupKey);
&#13;
简单但效率较低,在lodash中(可能比ES5更有效,并且最有可能优于ES6,目前没有jsPerf可用于测试假设)。
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.32.2/es6-shim.min.js"></script>
<pre id="out"></pre>
&#13;
function getLookupKey() {
var slice =_(arguments).dropWhile(_.isEmpty).dropRightWhile(_.isEmpty);
if (!slice.all(Boolean)) {
throw new SyntaxError('middle evaluates false');
}
return slice.join('.');
}
function log(str) {
document.getElementById('out').textContent += str + '\n';
}
var tests = [
[
[null, 'middle'], 'middle'
],
[
[null, null, null, 'middle', null, null], 'middle'
],
[
[null, null, 'middle', null, null, null], 'middle'
],
[
['prefix1', 'prefix2', 'middle', 'suffix1', 'suffix2'], 'prefix1.prefix2.middle.suffix1.suffix2'
],
[
[null, 'prefix', 'middle', 'suffix', null], 'prefix.middle.suffix'
],
[
['name'], 'name'
],
[
[null], ''
],
[
[null, null, null, null], ''
],
[
['a', null, null, null], 'a'
],
[
[null, null, null, 'b'], 'b'
],
[
[], ''
],
[
['prefix', null, 'suffix'], Error
],
[
[null, null, 'a', 'b', 'c', null, null], 'a.b.c'
],
[
['a', null, 'b'], Error
],
[
[null, 'middle', null], 'middle'
],
[
[null, 'middle1', null, 'middle2', 'middle3', null], Error
],
[
[null, 'middle1', 'middle2', 'middle3', null, 'middle4', 'middle5', null], Error
],
[
[null, 'middle1', null, null, null, null, 'middle2', null], Error
]
];
function test(fn) {
var length = tests.length,
index,
expected,
args,
actual;
for (index = 0; index < length; index += 1) {
args = tests[index][0];
expected = tests[index][1];
if (typeof expected === 'string') {
try {
actual = fn.apply(null, args);
} catch (e) {
actual = e.message;
}
log('Test ' + index + ': Expected: "' + expected + '" Actual: "' + actual + '"');
} else if (expected === Error) {
expected = 'middle evaluates false';
try {
actual = fn.apply(null, args);
} catch (e) {
actual = e.message;
}
log('Test ' + index + ': Expected: "' + expected + '" Actual: "' + actual + '"');
} else {
log('Test ' + index + ': coder error');
}
}
}
test(getLookupKey);
&#13;
答案 1 :(得分:1)
有趣的问题,这是我对解决方案的看法。我使用dropWhile
和dropRightWhile
来完成大部分繁重工作。
dropWhile
将从左侧删除所有false值,直到找到真值。 dropRightWhile
将从右侧删除所有错误的值,直到找到真值。 any
方法用于测试成员中是否存在任何错误值。
isFalse
方法只是检查falsey值的方法。它只是一个js
技巧,可以将任何值转换为布尔值。
function log(value) {
document.getElementById("output").innerHTML += JSON.stringify(value, null, 2) + "\n"
}
function getLookupKey() {
var keyParts = trimArrayFalsey(_.toArray(arguments));
if (_.any(keyParts, isFalse)) {
throw new Error('Center null');
}
return keyParts.join('.');
}
function trimArrayFalsey(arr) {
return _.chain(arr)
.dropWhile(isFalse)
.dropRightWhile(isFalse)
.value();
}
function isFalse(value) {
return !!!value;
}
log(getLookupKey('prefix', 'prefix', 'middle', 'suffix', 'suffix'));
// => 'prefix.prefix.middle.suffix.suffix'
log(getLookupKey(null, 'prefix', 'middle', 'suffix', null));
// => 'prefix.middle.suffix'
log(getLookupKey('name'));
// => 'name'
log(getLookupKey());
// => ''
log(getLookupKey('prefix', null, 'suffix'));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
<pre id="output"></pre>
&#13;
答案 2 :(得分:1)
dropWhile()和dropRightWhile()是您的朋友:
function getLookupKey() {
var falsey = _.negate(_.identity);
var result = _(arguments)
.dropWhile(falsey)
.dropRightWhile(falsey)
.join('.');
if (_.includes(result, '..')) {
throw new Error('invalid');
} else if (_.isUndefined(result)) {
result = '';
}
return result;
}
答案 3 :(得分:0)
实现:
function getLookupKey(){
var keyParts = trimArrayFalsey(_.toArray(arguments));
if (! _.isArray(keyParts) || ! _.all(keyParts)) {
throw new Error('Center null');
}
return keyParts.join('.');
}
依赖方法:
var _ = require("lodash");
/**
* getArrayRightFalsyCount(array, [predicate=_.identity])
* Get a count of the contiguous falsey members on the right of the array.
*/
function getArrayRightFalsyCount(arr, predicate){
if (! _.isArray(arr)) return 0;
predicate = predicate || _.identity;
return _.reduce(arr, function(result, value, index){
return predicate(value) ? 0 : result + 1;
}, 0);
}
/**
* getArrayLeftFalsyCount(array, [predicate=_.identity])
* Get a count of the contiguous falsey members on the left of the array.
*/
function getArrayLeftFalsyCount(arr, predicate){
if (! _.isArray(arr)) return 0;
return _.reduceRight(arr, function(result, value, index){
return value ? 0 : result + 1;
}, 0);
}
/**
* trimArrayFalsey(array, [predicate=_.identity])
* Trims the contiguous falsey members from the left and
* right edge of the array, but not from the middle.
*/
function trimArrayFalsey(arr, predicate){
if (! _.isArray(arr)) return null;
return _.slice(
arr,
getArrayLeftFalsyCount(arr, predicate),
arr.length - getArrayRightFalsyCount(arr, predicate)
);
}
测试它:
getArrayRightFalsyCount(["a", false, "b", false, null, "", 0]);
// => 4
getArrayLeftFalsyCount([0, "", null, false, "b", false, "a"]);
// => 4
trimArrayFalsey([]);
// => []
trimArrayFalsey(["a", "b", "c"]);
// => ["a", "b", "c"]
trimArrayFalsey([null, null, false, "a", null, "b", "c", 0, ""]);
// => ["a", null, "b", "c"]
getLookupKey(null, null, "a", "b", "c", null, null);
// => "a.b.c"
getLookupKey("a", null, "b");
// => Uncaught Error: Center null