user2634156
user2634156

Reputation: 1751

JavaScript move an item of an array to the front

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?

Upvotes: 169

Views: 192208

Answers (29)

Flying Turtle
Flying Turtle

Reputation: 1

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

Upvotes: 0

KooiInc
KooiInc

Reputation: 122888

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

Upvotes: 0

Alisa Sireneva
Alisa Sireneva

Reputation: 161

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.

Upvotes: 0

Trideep Sardar
Trideep Sardar

Reputation: 1

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

)

Upvotes: 0

G-T
G-T

Reputation: 131

The most readable way in my opinion.

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

Upvotes: 5

Adam Pietrasiak
Adam Pietrasiak

Reputation: 13184

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

Upvotes: 2

Joshua
Joshua

Reputation: 119

function unshiftFrom(arr, index) {
  if (index > -1 && index < arr.length) {    // validate index
    var [itemToMove] = arr.splice(index, 1)
    arr.unshift(itemToMove)
  }
  return arr   // optional
}

Upvotes: 0

jasonleonhard
jasonleonhard

Reputation: 13887

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

Upvotes: 0

giddygitau
giddygitau

Reputation: 51

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

Upvotes: 0

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

Upvotes: -3

Petter S&#230;len
Petter S&#230;len

Reputation: 113

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,

Upvotes: 7

JPR
JPR

Reputation: 642

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

Upvotes: 10

Lukasz Matysiak
Lukasz Matysiak

Reputation: 911

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

Upvotes: 0

Max Kurapov
Max Kurapov

Reputation: 165

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

Upvotes: 3

Alex Cartier
Alex Cartier

Reputation: 11

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

Upvotes: 1

Rajender Joshi
Rajender Joshi

Reputation: 4205

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

Upvotes: 5

Nina Scholz
Nina Scholz

Reputation: 386520

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

Upvotes: 3

Jee Mok
Jee Mok

Reputation: 6556

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>

Upvotes: 2

Maldoror
Maldoror

Reputation: 489

Here is an immutable solution if needed :

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

Upvotes: 44

Tamo Maes
Tamo Maes

Reputation: 365

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

Upvotes: 14

Tandroid
Tandroid

Reputation: 358

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

Upvotes: 31

sdfsdf
sdfsdf

Reputation: 5590

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]

Upvotes: 126

Skylar Brown
Skylar Brown

Reputation: 3462

The cleanest solution in ES6 in my opinion:

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

Upvotes: 116

David Thomas
David Thomas

Reputation: 253308

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:

Upvotes: 74

Master Yoda
Master Yoda

Reputation: 531

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

Upvotes: -4

Dan Smolinske
Dan Smolinske

Reputation: 319

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

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

Upvotes: 3

La-comadreja
La-comadreja

Reputation: 5755

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.

Upvotes: -1

Guffa
Guffa

Reputation: 700162

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/

Upvotes: 146

Bergi
Bergi

Reputation: 664196

  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.

Upvotes: 2

Related Questions