根据每个其他元素对JavaScript数组进行排序

5

假设我有一个如下的数组:

var a = [94, "Neptunium", 2, "Helium", null, "Hypotheticalium", 64, "Promethium"];

偶数的数组索引与后面的奇数索引相关联。换句话说,94与“Neputunium”相关联,2与“Helium”等相关联。我如何根据偶数索引排序数组,但保留其后面的奇数索引值?这样,最终我会得到一个像下面这样的数组:

a = [null, "Hypotheticalium", 2, "Helium", 64, "Promethium", 94, "Neptunium"];

注意:我知道使用对象或ES6 Map(或者在这种情况下,使用数字作为索引的稀疏数组,如果没有null)会更加合适,但我只是用它来探索语言。谢谢任何帮助。


你可以首先这样做:var a = [ ['a',1 ] ['b',2] ] - Manu Masson
@Simon,我刚刚在下面给出了我的解决方案。 - user162097
4个回答

5
var grouped = [];
for (var i = 0; i < a.length; i += 2) {
    grouped.push([a[i], a[i+1]]);
}

grouped.sort(function (a, b) { return a[0] - b[0]; });

理想情况下,我建议从现在开始使用 grouped 结构,因为将组合项分组在一起似乎比依赖隐式相邻索引更有意义。但是,如果您需要再次解包:

var b = [];
for (var i = 0; i < grouped.length; i++) {
    b.push.apply(b, grouped[i]);
}

顺便说一句,我可能在错误的值上进行排序(数字而不是单词),但你明白我的意思... - deceze

5

由于JavaScript引擎调用sort的顺序并不一定相同(甚至在相同引擎版本之间也可能不同),因此您不能直接使用sort对该数组进行所描述的操作。

但是您可以使用mapfiltersortreduce来实现:

var a = [94, "Neptunium", 2, "Helium", null, "Hypotheticalium", 64, "Promethium"];
a = a
  .map(function(entry, index, array) {
    return (index % 2 === 1) ? null : {
      value: array[index + 1],
      index: entry
    };
  })
  .filter(function(entry) {
    return entry != null;
  })
  .sort(function(left, right) {
    return left.index - right.index; // Works even when either or both
                                     // indexes are null, PROVIDED
                                     // no non-null index is negative,
                                     // because `null` will coerce to 0
  })
  .reduce(function(acc, entry) {
    acc.push(entry.index, entry.value);
    return acc;
  }, []);
document.body.innerHTML = JSON.stringify(a);

map 允许我们生成一个对象数组以匹配项(和null)。 filter 允许我们移除nullsort 允许我们排序。 reduce 允许我们生成结果数组(因为我们不能直接使用map将一项映射到两个)。
如果偶数项可能有负值,则sort回调必须以不同的方式处理,因为它会将null排序在这些负索引之上(除非当然这就是您想要的)。
ES6版本更加简洁:(在Babel的REPL上实时演示)。
let a = [94, "Neptunium", 2, "Helium", null, "Hypotheticalium", 64, "Promethium"];
a = a
  .map((entry, index, array) => {
    return (index % 2 === 1) ? null : {
      value: array[index + 1],
      index: entry
    };
  })
  .filter(entry => entry != null)
  .sort((left, right) => left.index - right.index)
  .reduce((acc, entry) => {
    acc.push(entry.index, entry.value);
    return acc;
  }, []);
console.log(a);

看起来很聪明,但运行代码给我一个未排序的结果,尽管顺序不同:[2,“氦”,null,“假想元素”,94,“海王星”,64,“钷”] - user162097
1
@user162097:它是按名称排序的,你是想按数字排序吗?编辑:啊,你是这个意思,你说“偶数”索引。很容易解决。 - T.J. Crowder
哦,对不起。我确实是想按数字排序,但我可以自己解决这个问题。谢谢。我想知道是否有更有效的方法使用老式算法(特别是对于非常大的数组),而不是依赖所有那些函数调用(并在此过程中将我的结构或缺乏结构更改为其他东西,即使更合理),但是很好的答案。 - user162097
@user162097:不使用内置的sort,不行,除非对于数组有很大的假设(例如,没有两个偶数项具有相同的值),但是您当然可以实现自己的排序函数。不能使用内置的sort的原因是它不会给您调用元素的索引,因此您无法将奇数项与偶数项保持在一起。 - T.J. Crowder
为什么使用map和filter?只是为了避免使用reduce吗? - Nina Scholz
@NinaScholz:差不多。我不喜欢使用reduce来生成数组或对象,但当我到达结尾并没有太多选择时,回到开头使用reduce也是合理的选择。 - T.J. Crowder

1
你需要对数组进行分组、适当的排序和重新组织。

var array = [94, "Neptunium", 2, "Helium", null, "Hypotheticalium", 64, "Promethium"],
    sorted = array.reduce(function (r, a, i) {
        i % 2 ? r[r.length - 1].push(a) : r.push([a]);
        return r;
    }, []).sort(function (a, b) {
        return a[0] - b[0];
    }).reduce(function (r, a) {
        return r.concat(a);
    });
document.write('<pre>' + JSON.stringify(sorted, 0, 4) + '</pre>');


0

我喜欢那些针对JS的答案背后的思路,但这是我的解决方案,它不依赖于本地的sort函数,而是基于侏儒排序的实现:

var a = [94, "Neptunium", 2, "Helium", null, "Hypotheticalium", 64, "Promethium"];
for (var i = 0, temp; i < a.length; i += 2) {
    if (a[i] > a[i + 2]) {
        temp = a[i];
        a[i] = a[i + 2];
        a[i + 2] = temp;
        temp = a[i + 1];
        a[i + 1] = a[i + 3];
        a[i + 3] = temp;
        i = i - 4;
    }
}
alert(JSON.stringify(a));

当然,这不是最快的排序算法,但只是一个概念证明,展示了可以在不改变我的结构缺陷和不进行大量(可能昂贵)的函数调用的情况下完成它(尽管我同意函数式编程更多地是JS习惯用语)。为了DRY,我可以将交换代码放入自己的函数中,并进行两次调用,如下所示:

var a = [94, "Neptunium", 2, "Helium", null, "Hypotheticalium", 64, "Promethium"];
function arraySwapper(array, index1, index2) {
    var temp = array[index1];
    array[index1] = array[index2], array[index2] = temp;
}
for (var i = 0, j; i < a.length; i += 2) {
    if (a[i] > a[i + 2]) {
        for (j = i; j < i + 2; j++) { arraySwapper(a, j, j + 2); }
        i -= 4;
    }
}
alert(JSON.stringify(a));


1
简而言之:您可以实现任何您想要的排序算法,其中有许多种,当您交换项目时,只需同时交换“i”和“i + 1”。 - deceze
@deceze 没错!嗯,几乎没错:你还要跳过两个索引,因为每隔一个索引不用于比较。 - user162097

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接