JavaScript如何将一个数组元素移到数组的第一位?

163

我想要检查一个数组是否包含"role"。如果有,我想把"role"移到数组的最前面。

var data= ["email","role","type","name"];
if ("role" in data) data.remove(data.indexOf("role")); data.unshift("role")
data;

这里是我的结果:

["role", "email", "role", "type", "name"]

我该怎么解决这个问题?


可能是通过值从数组中删除项目的重复问题。 - Miklos Aubert
2
@jraede: 当然是有效的。 - Bergi
那个remove方法是什么?你是在使用Prototype.js或类似的东西吗? - Bergi
if('role' in data) 可以翻译为:如果数据中包含“角色”。data.remove() 可以翻译为:删除数据。 - jraede
29个回答

144

你可以对数组进行排序,并指定值"role"在所有其他值之前,而所有其他值都相等:

var first = "role";
data.sort(function(x,y){ return x == first ? -1 : y == first ? 1 : 0; });

示例:http://jsfiddle.net/Guffa/7ST24/


37
这可能会改变其他项目的顺序,因为“sort”不是稳定的。 - Bergi
@Bergi:好观点,这是需要考虑的事情。这取决于实现方式,而且似乎大多数浏览器都有稳定的排序功能:https://dev59.com/QnA75IYBdhLWcg3w790Y - Guffa
11
工作正常,但嵌套第三层会使代码难以理解。 - Waltari
1
除了“角色”之外,是否有可能对剩余元素进行排序? - Jeya Suriya Muthumari
3
ECMAScript 2019现在要求Array#sort稳定 - str
显示剩余2条评论

112

在我看来,ES6中最简洁的解决方案:

let data = ["email","role","type","name"];
data = data.filter(item => item !== "role");
data.unshift("role");

15
在将角色重新添加到列表中之前,需要检查该角色是否已从列表中删除。 - agermano
2
这不是对现有数组进行排序。它假设 role 已经存在,保留其他元素,然后强制在开头添加一个硬编码的 "role"。 - Hugolpz

109
let data = [0, 1, 2, 3, 4, 5];
let index = 3;
data.unshift(data.splice(index, 1)[0]);
// data = [3, 0, 1, 2, 4, 5]

16
这是目前为止最好的答案,因为它依赖于索引而不是值。因此,无论是那些希望依赖于索引的人,还是那些希望依赖于值的人(他们只需用data.findIndex(value)替换index),都可以使用它。 - goodvibration
1
是的,如果你知道要移动的元素的索引,这是最好的答案。问题是如何移动具有特定值的元素。因此,对于这种情况,这不是最好的答案。最好使用过滤方法,就像上面的答案中所示。 - Karim Shaloh
这种方法在小数组上快了36%。 - undefined

74

我的第一个想法是:

var data= ["email","role","type","name"];

// if it's not there, or is already the first element (of index 0)
// then there's no point going further:
if (data.indexOf('role') > 0) {
    // find the current index of 'role':
    var index = data.indexOf('role');
    // using splice to remove elements from the array, starting at
    // the identified index, and affecting 1 element(s):
    data.splice(index,1);
    // putting the 'role' string back in the array:
    data.unshift('role');
}

console.log(data);

进行修订和整理:

if (data.indexOf('role') > 0) {
    data.splice(data.indexOf('role'), 1);
    data.unshift('role');
}

参考文献:


13
我认为我们可以将代码变得更好一些,不必两次使用 Array.indexOf() 函数,因为这意味着它会对整个数组进行两次搜索。const roleIndex = data.indexOf('role'); if (roleIndex > 0) { data.splice(roleIndex, 1); data.unshift('role'); }修改后的代码如下所示:const roleIndex = data.indexOf('role'); if (roleIndex > 0) { data.splice(roleIndex, 1); data.unshift('role'); } - digiwand
1
比排序更合理。 - MajidTaheri
3
data.splice returns just removed part, which you can immediately use to insert, making it 1 line instead of 2, and also making it useful for more complicated cases like for an array of objects: data.unshift(data.splice(roleIndex, 1)[0]) - Maxim Georgievskiy

38

如果需要,这里有一个不可变的解决方案:

      const newData = [
          data.find(item => item === 'role'),
          ...data.filter(item => item !== 'role'),
        ],

1
如果没有任何元素等于“role”,那么数组的第一个元素将是“undefined”。 - Jan Jůna
你是正确的,在这篇文章中,他在将'role'推到第一位之前使用了if ("role" in data)。我的回答不够详细,但它隐含着在更新数组之前需要进行检查的要求。 - Maldoror
2
谢谢,这个在函数式编程的纯度方面更好。 如果有多个具有“角色”的元素,该解决方案只会返回顶部的第一个元素,因为find函数只返回一个元素。 一个通用的解决方案可能是: const sortedCards = [...data.filter((item) => item === 'NORI'),...cards.filter((item) => item !== 'NORI')]; - Ramy El-Basyouni

30

如果您不想更改现有数组,可以使用ES6解构和filter方法创建一个新副本,同时保持其他项的顺序。

const data = ["email", "role", "type", "name"];
const newData = ['role', ...data.filter(item => item !== 'role')];

这里忘记在重新添加角色之前检查是否已从列表中删除。 - Yves M.

13
如果您有一个对象数组,可以使用splice和push来移动起始索引。 Splice用从所需索引开始的数组部分替换原始数组,并返回它删除的部分(索引之前的内容),然后您将其推送。

let friends = [{
    id: 1,
    name: "Sam",
  },
  {
    id: 2,
    name: "Steven",
  },
  {
    id: 3,
    name: "Tom",
  },
  {
    id: 4,
    name: "Nora",
  },
  {
    id: 5,
    name: "Jessy",
  }
];

const tomsIndex = friends.findIndex(friend => friend.name == 'Tom');
friends.push(...friends.splice(0, tomsIndex));

console.log(friends);


这行代码的作用是什么:friends.push(...friends.splice(0, tomsIndex)); - Sharath
这段代码完美运行,节省了我的时间,非常感谢。 - Vishali

10
要检查一个项目是否存在于数组中,您应该使用.includes()而不是in(如此处已注意到,in用于对象中的属性)。
这个函数会做你想要的事情:(从当前位置删除该项并向前读取)

   
   data = ["email","role","type","name"];
   moveToFirst("role", data);
    
   function moveToFirst( stringToMove, arrayIn ){
      if ( arrayIn.includes(stringToMove) ){
        let currentIndex = arrayIn.indexOf(stringToMove);
        arrayIn.splice(currentIndex, 1);
        arrayIn.unshift(stringToMove);
      } 
    }
    
    console.log(data);


你听说过 clean-code 吗?真的,我不明白为什么人们要使用变量名和参数的缩写... - Ilker Cat
复制/粘贴时选择的文本稍微少一些,而不需要理解底层代码。 - JPR

7
类似于@Tandroid的回答,但更为普遍的解决方案:
const putItemsFirst = ({ findFunction, array }) => [
    ...array.filter(findFunction), 
    ...array.filter(item => !findFunction(item)),
]; 

可以像这样使用

putItemsFirst({ 
    array: ["email","role","type","name"], 
    findFunction: item => item === 'role',
})

我最终使用的是类似于这样的东西,


5

我会选择这个ES6的方式。它不会改变原始数组(只考虑没有嵌套的情况),也不会遍历数组(filter),你不仅仅只能使用0索引来移动数组项。

const moveArrayItem = (array, fromIndex, toIndex) => {
    const arr = [...array];
    arr.splice(toIndex, 0, ...arr.splice(fromIndex, 1));
    return arr;
}


const arr = ["a", "b", "c", "d", "e", "f", "g"];
console.log(moveArrayItem(arr, 4, 0))
// [ 'e', 'a', 'b', 'c', 'd', 'f', 'g' ]

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