我认为代码(下面)已经过优化(只使用比我相同逻辑的初始版本更少的变量)。
我如何才能真正了解其是否经过适当优化?
在优化过程中我应该考虑哪些因素?
这是代码( also on jsfiddle)
function process(arr){
var processed = [];
for(var i=0,len=arr.length;i<len;i++){
if(processed.indexOf(arr[i]) < 0){
var nodes = findIndexes(arr,arr[i]);
if(nodes.length > 1){
for(var j=0,jlen=nodes.length;j<jlen;j++){
arr[nodes[j]] = arr[nodes[j]] + '(' + ( j + 1 ) + ')';
}
}
processed.push(arr[i]);
}
}
return arr;
}
function findIndexes(arr,val){
var node = [];
for(var i=0,len=arr.length;i<len;i++){
if(arr[i] === val){
node.push(i);
}
}
return node;
}
// input
var arr = ['aa','bb','bb','aa','cc','dd','cc','ff']
console.log(process(arr));
//output: ["aa(1)", "bb(1)", "bb(2)", "aa(2)", "cc(1)", "dd", "cc(2)", "ff"]
这是代码的解释。 'process'函数在数组中查找相同的值,并且对于每个相同的值,它通过post将数字更改为该值来更改值,“number”表示在数组中找到的值的计数。
例如
arr = [“x”,“x”,“y”,“z”]将返回[“x(1)”,“x(2)”,“y”,“z”]
“y”和“z”没有变化,因为它们只出现过一次。
为了优化,我使用了一个名为processed的数组,用于保存刚刚在main for循环中处理的值,因此在下一次迭代中可以确定新的迭代值已经处理或不是通过检查array.indexOf方法,如果值已经处理,那么它可以安全地跳过底层逻辑(if / for语句)。
现在我不知道除了改变整个过程逻辑之外如何进一步优化它。
答案 0 :(得分:4)
广义上的优化将涉及简化代码,预先计算重复使用的结果,以及组织代码以便可以重复使用更多结果。
根据分析结果生成您的小提琴代码。
Logical LOC: 26
Mean parameter count: 3
Cyclomatic complexity: 7
Cyclomatic complexity density: 27%
Maintainability index: 104
代码行(LOC) - 表示代码中的近似行数。计数基于IL代码,因此不是源代码文件中的确切行数。非常高的计数可能表明某种类型或方法正在尝试做太多工作而应该拆分。它也可能表明类型或方法可能难以维护。
可维护性指数 - 计算0到100之间的索引值,表示维护代码的相对容易程度。高价值意味着更好的可维护性。彩色编码评级可用于快速识别代码中的故障点。绿色评级介于20和100之间,表示代码具有良好的可维护性。黄色等级在10到19之间,表示代码可以适度维护。红色等级是0到9之间的等级,表示可维护性低。
Cyclomatic Complexity - 衡量代码的结构复杂性。它是通过计算程序流程中不同代码路径的数量来创建的。具有复杂控制流程的程序将需要更多测试以实现良好的代码覆盖并且将难以维护。
使用online tool为您的javascript代码检查代码复杂性。
Reference(为您提供优化时应记住的不同技巧)
答案 1 :(得分:3)
你可以在一个循环中完成:
function process2(arr) {
var out = arr.slice(0),
seen = {},
len = arr.length,
i, key, item, count;
for (i = 0; i < len; ++i) {
key = out[i];
item = seen[key];
if (!item) {
// firstIndex, count
seen[key] = item = [i, 0];
}
count = ++item[1];
if (count > 1) {
if (count === 2) {
out[item[0]] = key + '(1)';
}
out[i] = key + '(' + count + ')';
}
}
return out;
}
// input
var arr = ['aa', 'bb', 'bb', 'aa', 'cc', 'dd', 'cc', 'ff']
console.time('p2');
console.log(process2(arr));
console.timeEnd('p2');
从基准测试开始,process2
比process1
快约2倍。对于这个问题,这只是一个非常天真的第一次传递。
答案 2 :(得分:2)
另一种使用较少更改来优化代码的方法:
在您的特定情况下,您将浏览每个新找到的条目的整个数组,尽管之前的所有条目都已处理过,因此可以通过将当前索引传递给findIndexes来进一步优化:
function findIndexes(arr,val, fromIndex){
var node = [];
for(var i=fromIndex,len=arr.length;i<len;i++){
if(arr[i] === val){
node.push(i);
}
}
return node;
}
答案 3 :(得分:1)
目前,您的代码具有O(n^2)
完整性。这是由arr
中process
的外部循环引起的,然后调用findIndexes
再次循环遍历arr
。
您可以将其简化为循环数组两次的O(n)
算法:
function process(arr) {
var result = [];
var counter = {}, counts = {};
var len = arr.length;
for(var i = 0; i < len; i++){
var value = arr[i];
counter[value] = 1;
counts[value] = (counts[value] || 0) + 1;
}
for(var i = 0; i < len; i++){
var value = arr[i];
if(counts[value] == 1) {
result.push(value);
} else {
result.push(value + "(" + counter[value]++ + ")");
}
}
return result;
}
答案 4 :(得分:1)
这是一个不使用嵌套循环的示例,并使用对象存储密钥信息:
var obj = {};
// loop over the array storing the elements as keys in the object
// if a duplicate element is found, increment the count value
for (var i = 0, l = arr.length; i < l; i++) {
var key = arr[i];
if (!obj[key]) obj[key] = { count: 0, level: 0 };
obj[key].count++;
}
// remove all the key/values where the count is 1
// ie there are no duplicates
for (var p in obj) {
if (obj[p].count === 1) delete obj[p];
}
// for each element in the original array, increase its 'level'
// amend the element with the count
// reduce the count
for (var i = 0, l = arr.length; i < l; i++) {
var key = arr[i];
if (obj[key] && obj[key].count > 0) {
obj[key].level++;
arr[i] = key + '(' + obj[key].level + ')';
obj[key].count--;
}
}