167

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?

4
  • possible duplicate of Remove item from array by value Commented May 28, 2014 at 21:00
  • 2
    @jraede: Of course it's valid?
    – Bergi
    Commented May 28, 2014 at 21:00
  • What is that remove method? Are you using Prototype.js or something similar?
    – Bergi
    Commented May 28, 2014 at 21:01
  • if('role' in data) and data.remove()?
    – jraede
    Commented May 28, 2014 at 21:29

31 Answers 31

145

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/

7
  • 38
    That might change the order of the other items though, sort is not stable.
    – Bergi
    Commented May 28, 2014 at 21:02
  • @Bergi: Good point, that's something to consider. That is implementation dependant, and it seems most browsers have a stable sort: stackoverflow.com/questions/3026281/…
    – Guffa
    Commented May 28, 2014 at 21:06
  • 12
    Works, but nesting tertiarys makes for fairly hard to understand code
    – Waltari
    Commented Oct 11, 2017 at 13:57
  • 1
    Is there any possible to sort the remaining elements other than "role" Commented Sep 19, 2018 at 5:40
  • 3
    ECMAScript 2019 now requires Array#sort to be stable.
    – str
    Commented Sep 24, 2019 at 9:26
121
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]
3
  • 19
    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)). Commented Dec 26, 2019 at 11:05
  • 1
    Yes, this is the best answer if you know the index of the element you want to move. The question is how to move an element with a specific value. Therefore, this isn't the best answer for this case. It would be better to use the filter method like in the above answer. Commented Jun 19, 2023 at 17:46
  • This method 36% faster on small arrays Commented Sep 4, 2023 at 14:40
115

The cleanest solution in ES6 in my opinion:

let data = ["email","role","type","name"];
data = data.filter(item => item !== "role");
data.unshift("role");
2
  • 15
    This forgets to check if role was removed from the list before adding it back.
    – agermano
    Commented Apr 8, 2021 at 16:42
  • 2
    This is not sorting the existing array. This assumes role IS present, keep other elements, then forcefully adds a hard coded "role" at the beginning.
    – Hugolpz
    Commented Nov 8, 2022 at 19:40
74

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:

3
  • 13
    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'); } ```
    – digiwand
    Commented Feb 24, 2017 at 19:59
  • 1
    It is more reasonable than sorting. Commented Jan 26, 2018 at 6:17
  • 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])
    – Maxím G.
    Commented Sep 17, 2019 at 21:37
42

Here is an immutable solution if needed :

      const newData = [
          data.find(item => item === 'role'),
          ...data.filter(item => item !== 'role'),
        ],
3
  • 1
    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
    Commented Sep 12, 2019 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
    Commented Sep 13, 2019 at 9:16
  • 2
    thanks, this is better in terms in functional programming purity. Adding another thing if there is multiple elements with 'role', the solution will only return the first one on the top because find function is returning only one element. A general solution may be: const sortedCards = [...data.filter((item) => item === 'NORI'),...cards.filter((item) => item !== 'NORI')]; Commented Aug 5, 2021 at 13:32
31

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')];
1
  • This forgets to check if role was removed from the list before adding it back.
    – Yves M.
    Commented Jan 7, 2022 at 10:27
14

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

2
  • What exactly does this line do: friends.push(...friends.splice(0, tomsIndex));
    – Sharath
    Commented Feb 18, 2021 at 11:04
  • This code works exactly,saved my time thanks alot
    – Vishali
    Commented Aug 4, 2022 at 11:46
10

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

2
  • Did you ever hear of clean-code? Really, I don't get why people use abbreviations for variable names and params ...
    – Ilker Cat
    Commented Feb 16, 2021 at 20:50
  • Slightly less text to select when copy/pasting without understanding the underlying code
    – JPR
    Commented Feb 28, 2021 at 7:19
7

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,

0
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' ]
4

The most readable way in my opinion.

array.sort((a, b) => (a === value && -1) || (b === value && 1) || 0)
3
var data= ["email","role","type","name"];

data.splice(data.indexOf("role"), 1);
data.unshift('role');
1
  • 1
    Does not work correctly if the original array does not contain "role".
    – 000
    Commented May 28, 2014 at 21:24
3

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

2
  • This might lead to unexpected results as ECMAScript before 2019 did not require Array#sort to be stable. According to MDN, Internet Explorer and Edge do not have a stable sorting algorithm.
    – str
    Commented Sep 24, 2019 at 9:28
  • Edge supports stable sort as of version 79! Commented Jun 16, 2020 at 14:55
3

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"))
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.
2

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>

2

My solution is a bit different as it mutates original array instead of creating a new one.

It will move given item to start of the array and move item that was previously at start in the place of requested item.

function moveElementToStart<T>(items: T[], item: T) {
  const itemIndex = items.indexOf(item);

  // Item is not found or it is already on start
  if (itemIndex === -1 || itemIndex === 0) return;

  // Get item that is currently at start
  const currentItemAtStart = items[0];
  
  // Swap this item position with item we want to put on start
  items[0] = item;
  items[itemIndex] = currentItemAtStart;
}
1

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);
1
  • Simple and work perfectly like what i want
    – Jhonny Jr.
    Commented Aug 19, 2022 at 6:50
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"]
0

const moveToFront = (arr, queryStr) =>
  arr.reduce((acc, curr) => {
    if (queryStr === curr) {
      return [curr, ...acc];
    }
    return [...acc, curr];
  }, []);

const data = ['email', 'role', 'type', 'name'];

console.log(moveToFront(data, 'role'))

0
const moveTargetToBeginningOfArray = (arr, target) => {
    // loop through array
    for (let i = 0; i < arr.length; i++){
        // if current indexed element is the target
        if(arr[i] === target){
            // remove that target element
            arr.splice(i, 1)
            // then add a target element to the beginning of the array
            arr.unshift(target)
        }
    }
    return arr;
};

// quick sanity check, before and after both are correct
const arrayOfStrings = ["email", "role", "type", "name", "role", "role"];
console.log('before:', arrayOfStrings)
console.log('after:', moveTargetToBeginningOfArray(arrayOfStrings, "role"))

// this would also work for numbers
var arrayOfNumbers = [2,4,0,3,0,1,0]
console.log('before:', arrayOfNumbers)
console.log('after:', moveTargetToBeginningOfArray(arrayOfNumbers, 0))
1
  • Also, it is really easy to put the targets at the end of the array by simply changing the word "unshift" to "push" Commented Dec 15, 2021 at 21:22
0
function unshiftFrom(arr, index) {
  if (index > -1 && index < arr.length) {    // validate index
    var [itemToMove] = arr.splice(index, 1)
    arr.unshift(itemToMove)
  }
  return arr   // optional
}
0

//we can do this from scratch

let tempList=["person1","person2","person3"];
let result=[];
//suppose i need to move "person2" to first place
let movableValue=null;
let query="person2"; //here you could use any type of query based on your problem

tempList.map((e)=>{
 if(e!==query){
  result.push(e);
 }else if(e===query){
 movableValue=e;
 }
})

if(movableValue!==null){
result.unshift(movableValue);
}

console.log(result)

)

0
function moveArrayItemToTheTop(array, index){
    return [array[index], ...array.filter((item, i) => {return i !== index})];
}
0

The existing answers shift the array twice: first to the left (removing one element), then to the right (adding one element to the beginning). While this is simple, it's quite inefficient whenever the array is large and the element you want to move is close to the beginning. For example, this is typically the case for move-to-front transform, and an inefficient algorithm would result in a significant loss of performance.

Instead, we can only rotate the prefix of the array up to the first occurrence of the element.

There are two trivial ways to do that. One is to implement the rotation algorithm ourselves. Another is to utilize the copyWithin primitive, which should translate to memcpy with JIT, giving near native performance. Here are the two implementations:

function moveToFront1(array, element) {
  const index = array.indexOf(element);
  let heldElement = element;
  for (let i = 0; i <= index; i++) {
    const tmp = array[i];
    array[i] = heldElement;
    heldElement = tmp;
  }
}

function moveToFront2(array, element) {
  const index = array.indexOf(element);
  array.copyWithin(1, 0, index);
  array[0] = element;
}

const exampleArray1 = ["lorem", "ipsum", "dolor", "sit", "amet"];
moveToFront1(exampleArray1, "dolor");
console.log(exampleArray1);

const exampleArray2 = new Uint8Array([1, 2, 3, 4, 5]);
moveToFront2(exampleArray2, 3);
console.log(exampleArray2);

In my tests, the second version is significantly faster than the first version for typed arrays. For untyped arrays, it is either a bit slower (SpiderMonkey) or much, much slower (V8). So the rule of thumb is: use the former for untyped arrays, the latter for typed arrays.

As a bonus, this code can be easily adjusted if the index of the value is already known.

0

How about:

const data = ["email","role","type","name"];
const noRole = ["email","type","name"];

const move2Front = (arr, el2Move) => 
  arr.filter(v => v === el2Move).concat(arr.filter(v => v !== el2Move));

// non mutating
console.log(`${move2Front(data, `role`)}`);
console.log(`${move2Front(noRole, `name`)}`);

// mutating
let data2 = ["email","role","type","name"];
data2 = move2Front(data2, `name`);
console.log(`${data2}`)

0

This is immediate, through the use of a TrueSet representing each item with the position the item is expected to assume.

const 
    repr = item => item==='role'? 0: 1,
    got = TrueSet.of(repr, ORDER.ASCENDING, ORDER.INSERTION)
        .letAll(data)
            .toArray();   

The role item is expected to occupy position 0, while the other items are expected to occupy position 1. The positions are arranged in ascending order. All items sharing the same position maintain their order of insertion. The role item may or may not be present in the array.

You can check the validity of the proposed solution by:

import {TrueSet} from "@ut8pia/classifier/queue/TrueSet.js";
import {ORDER} from "@ut8pia/classifier/global.js";
import assert from "assert";

const 
    data = ["email","role","type","name"],
    expected = ["role","email","type","name"];

assert.deepEqual(got, expected)      

If you are unfamiliar with TrueSet and the representation function, exploring the @ut8pia/classifier library's online documentation could be helpful.

0
function moveValue(array, chosen, moveTo) {
  const copy = [];
  let index = 0;

  for (let i = 0; i < array.length; i++) copy[i] = array[i];

  for (let i = 0; i < array.length; i++) {
    if (i == chosen) index += 1;
    array[moveTo] = copy[chosen];
    array[i] = copy[index];

    if (i >= moveTo) {
      array[i] = copy[i - 1];
      if (i > chosen) {
        array[i] = copy[i];
      }
    }
    index += 1;
  }
  return array;
}
1
  • This function can move any value to any location within an array, including the back and front. Commented May 8 at 4:16
-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.

1
  • 2
    Of course you should use it. Just shim it for those (dead) browsers.
    – Bergi
    Commented May 28, 2014 at 21:02
-3

data.unshift(data.splice(data.indexOf('role'), 1)[0])

data.indexOf('role') will find the index of 'role' in the array and then the original array is spliced to remove the 'role' element, which is added to the beginning of the array using unshift

Not the answer you're looking for? Browse other questions tagged or ask your own question.