172

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? Commented May 28, 2014 at 21:00
  • What is that remove method? Are you using Prototype.js or something similar? Commented May 28, 2014 at 21:01
  • if('role' in data) and data.remove()? Commented May 28, 2014 at 21:29

29 Answers 29

146

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/

Sign up to request clarification or add additional context in comments.

7 Comments

That might change the order of the other items though, sort is not stable.
@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/…
Works, but nesting tertiarys makes for fairly hard to understand code
Is there any possible to sort the remaining elements other than "role"
ECMAScript 2019 now requires Array#sort to be stable.
|
130
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]

4 Comments

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)).
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.
This method 36% faster on small arrays
@goodvibration It would be data.indexOf(value) rather than data.findIndex(value), because findIndex() takes a testing function rather than a value (though that makes findIndex() useful for more complex tests, like checking a property of an object with something like data.findIndex(item => item.property === value)).
116

The cleanest solution in ES6 in my opinion:

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

2 Comments

This forgets to check if role was removed from the list before adding it back.
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.
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 Comments

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'); } ```
It is more reasonable than sorting.
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])
45

Here is an immutable solution if needed :

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

3 Comments

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.
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)
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')];
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 Comment

This forgets to check if role was removed from the list before adding it back.
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);

1 Comment

What exactly does this line do: friends.push(...friends.splice(0, tomsIndex));
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 Comments

Did you ever hear of clean-code? Really, I don't get why people use abbreviations for variable names and params ...
Slightly less text to select when copy/pasting without understanding the underlying code
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,

Comments

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

Comments

5

The most readable way in my opinion.

array.sort((a, b) => (a === value && -1) || (b === value && 1) || 0)

Comments

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

data.splice(data.indexOf("role"), 1);
data.unshift('role');

1 Comment

Does not work correctly if the original array does not contain "role".
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 Comments

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.
Edge supports stable sort as of version 79!
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"))

Comments

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.

Comments

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>

Comments

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

Comments

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 Comment

Simple and work perfectly like what i want
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"]

Comments

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

Comments

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 Comment

Also, it is really easy to put the targets at the end of the array by simply changing the word "unshift" to "push"
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
}

Comments

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)

)

Comments

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.

Comments

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}`)

Comments

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 Comment

This function can move any value to any location within an array, including the back and front.
-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 Comment

Of course you should use it. Just shim it for those (dead) browsers.
-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

Comments

-4
var data= ["email","role","type","name"];
if ("role" in data) data.splice(data.indexOf("role"),1); data.unshift("role");
data;

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.