86

I want to check if an array contains "role". If it does, I want to move the "role" to the front of the array.

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

Here, I got the result:

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

How can I fix this?

Joel
  • 4,631
  • 9
  • 33
  • 51
user2634156
  • 921
  • 1
  • 6
  • 6

19 Answers19

89

You can sort the array and specify that the value "role" comes before all other values, and that all other values are equal:

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

Demo: http://jsfiddle.net/Guffa/7ST24/

Guffa
  • 640,220
  • 96
  • 678
  • 956
  • 19
    That might change the order of the other items though, `sort` is not stable. – Bergi May 28 '14 at 21:02
  • @Bergi: Good point, that's something to consider. That is implementation dependant, and it seems most browsers have a stable sort: http://stackoverflow.com/questions/3026281/array-sort-sorting-stability-in-different-browsers – Guffa May 28 '14 at 21:06
  • 6
    Works, but nesting tertiarys makes for fairly hard to understand code – Waltari Oct 11 '17 at 13:57
  • Is there any possible to sort the remaining elements other than "role" – Jeya Suriya Muthumari Sep 19 '18 at 05:40
  • Just want to note that if you google this JS fiddle link, you'll see that it got really popular on the internet. Thanks, this really helped me. – Nikita Fuchs Jul 15 '19 at 21:59
  • @NikitaFuchs: Interesting. However, it seems to be mostly mirrors of stackoverflow. I figure that you would get the same result searching for anything else from an answer as long as it is specific enough. – Guffa Aug 03 '19 at 23:00
  • ECMAScript 2019 now requires [`Array#sort` to be stable](https://www.ecma-international.org/ecma-262/10.0/#sec-array.prototype.sort). – str Sep 24 '19 at 09:26
73

The cleanest solution in ES6 in my opinion:

let data = ["email","role","type","name"];
data = data.filter(item => item !== "role");
data.unshift("role");
Skylar Brown
  • 2,272
  • 1
  • 12
  • 6
58

My first thought would be:

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);

To revise, and tidy up a little:

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

References:

David says reinstate Monica
  • 230,743
  • 47
  • 350
  • 385
  • 10
    I think we can make this a little better by not having to use the `Array.indexOf()` function twice since that means it's searching the entire array twice. ``` const roleIndex = data.indexOf('role'); if (roleIndex > 0) { data.splice(roleIndex, 1); data.unshift('role'); } ``` – Ariella Feb 24 '17 at 19:59
  • 1
    It is more reasonable than sorting. – MajidTaheri Jan 26 '18 at 06:17
  • `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 Sep 17 '19 at 21:37
46
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]
sdfsdf
  • 3,149
  • 3
  • 25
  • 52
  • 4
    This is by far the best answer here, because it relies on an index rather than a value. So it is useful for both those wishing to rely on an index, and those wishing to rely on a value (who can simply replace `index` with `data.findIndex(value)`). – goodvibration Dec 26 '19 at 11:05
20

If you don't want to alter the existing array, you can use ES6 destructuring with the filter method to create a new copy while maintaining the order of the other items.

const data = ["email", "role", "type", "name"];
const newData = ['role', ...data.filter(item => item !== 'role')];
Tandroid
  • 240
  • 2
  • 6
17

Here is an immutable solution if needed :

      const newData = [
          data.find(item => item === 'role'),
          ...data.filter(item => item !== 'role'),
        ],
Maldoror
  • 191
  • 1
  • 6
  • What if there is no element which would equal to "role"? In this case, you would have `undefined` as the first element of the array. – Jan Jůna Sep 12 '19 at 18:38
  • You are right, in this post he uses : `if ("role" in data)` before pushing 'role' to first place. My answer wasn't thorough but it implicitly asks for a check before (otherwise we don't update the array) – Maldoror Sep 13 '19 at 09:16
7

To check whether an item exists in an array you should to use .includes() instead of in (as already noted here, in is for properties in objects).

This function does what you are looking for: (removes the item from the position it is in and reads in front)

   
   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);
JPR
  • 432
  • 5
  • 10
  • Did you ever hear of [clean-code](https://medium.com/swlh/the-must-know-clean-code-principles-1371a14a2e75)? Really, I don't get why people use abbreviations for variable names and params ... – Ilker Cat Feb 16 '21 at 20:50
  • Slightly less text to select when copy/pasting without understanding the underlying code – JPR Feb 28 '21 at 07:19
6

If you have an array of objects you could shift the start-index with splice and push. Splice replaces the original array with the part of the array starting from the desired index and returns the part it removes (the stuff before the index) which you push.

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);
Tamo Maes
  • 235
  • 3
  • 5
5

I would go with this ES6 solution. It doesn't mutate the original array(considering it's not nested), doesn't traverse through the array(filter) and you're not just limited to 0th index for shifting the array item.

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' ]
Rajender Joshi
  • 3,914
  • 1
  • 20
  • 37
3

Similar to @Tandroid's answer but a more general solution:

const putItemsFirst = ({ findFunction, array }) => [
    ...array.filter(findFunction), 
    ...array.filter(item => !findFunction(item)),
]; 

Can be used like this

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

Something similar to this is what I ended up using,

2
  1. the in operator is about properties, not about items in arrays. See How do I check if an array includes an object in JavaScript? for what to use else.
  2. You're missing braces around the two (!) statements in your if-block
  3. I'm not sure whether that .remove() function you're using does take an index of an item.
Community
  • 1
  • 1
Bergi
  • 513,640
  • 108
  • 821
  • 1,164
2
var data= ["email","role","type","name"];

data.splice(data.indexOf("role"), 1);
data.unshift('role');
Dan Smolinske
  • 299
  • 3
  • 7
2

You could take the delta of the check with the wanted value at top.

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

data.sort((a, b) => (b === 'role') - (a === 'role'));

console.log(data);
Nina Scholz
  • 323,592
  • 20
  • 270
  • 324
  • This might lead to unexpected results as ECMAScript before 2019 did not require `Array#sort` to be stable. According to [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Browser_compatibility), Internet Explorer and Edge do *not* have a stable sorting algorithm. – str Sep 24 '19 at 09:28
  • Edge supports stable sort as of version 79! – Isaac Grynsztein Jun 16 '20 at 14:55
1

Using lodash _.sortBy. If the item is role, it will be sorted first, otherwise second. This works fine too if there is no role

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

var sorted = _.sortBy(data, function(item) {
  return item === 'role' ? 0 : 1;
});

console.log(sorted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Jee Mok
  • 4,537
  • 7
  • 35
  • 66
1

A reusable ES6/Typescript solution:

const moveToStart = <T>(array: T[], predicate: (item: T) => boolean): T[] => {
  return array.sort((a, b) => {
    if (predicate(a)) return -1;
    if (predicate(b)) return 1;

    return 0;
  });
};
const data = ["email", "role", "type", "name"];
const result = moveToStart(data, (item) => item === "role"))
Max Kurapov
  • 85
  • 1
  • 8
0

Just wanted to drop this on here since according to other comments Guffa's answer seems to be gaining traction, the final tertiary - which was one of the negative comments on that answer is unnecessary. Also using arrow functions makes it seem much cleaner.

Also, it is easily expandable to handling Arrays of objects.

const first = "role";
data.sort((x, y) => first === x ? -1 : first === y)

I believe this should also handle the worry of the rest of the array being affected. When the sort function returns a number less than 0 (first === x), the element will move toward the start of the Array, when it returns 0 (first !== y), there will be no movement, and when a number greater than 0 (first === y), x will move toward the end of the Array, all in relation to x and y. Therefore, when neither x or y are equivalent to the desired first element (or it's identifier in the case of sorting objects), there will be no movement of the two in relation to each other.

For an object:

const unsorted = [{'id': 'test'}, {'id': 'something'}, {'id': 'else'}];
const first = 'something';
const sorted = unsorted.sort((x,y) => x['id'] === first ? -1 : y['id'] === first);
0

Generalized one-liners:

const data = ["a", "b", "c", "d", "e", "f"];
const [from, take] = [3, 2];

data.unshift(...data.splice(from, take));
// alternatively
data = [...data.splice(from, take), ...data];
// ["d", "e", "a", "b", "c", "f"]
Lukasz Matysiak
  • 711
  • 5
  • 10
-1
var i = -1;
while (i < data.length) {
    if (data[i] === "role") {
        data.splice(i, 1);
        break;
    }
    i++;
}
data.unshift("role");

indexOf only has limited browser support, not being recognized by IE7-8. So I wouldn't use it if I were you, even at the expense of a few lines' worth of code conciseness. You also want to put a semicolon at the end of the "unshift" statement. splice()'s first argument specifies the index to start removing elements, and the second argument specifies the number of arguments to remove.

La-comadreja
  • 5,161
  • 8
  • 31
  • 59
-4
var data= ["email","role","type","name"];
if ("role" in data) data.splice(data.indexOf("role"),1); data.unshift("role");
data;
Master Yoda
  • 536
  • 8
  • 21
  • This has the same problems as in the question, and gives the same incorrect result. The "in" operator doesn't apply to Arrays, and you're missing curly braces around multiple statements intended to be conditionalized by the "if" statement. – Dan Korn May 28 '14 at 21:15