Reputation: 1751
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
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
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
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
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
Reputation: 131
The most readable way in my opinion.
array.sort((a, b) => (a === value && -1) || (b === value && 1) || 0)
Upvotes: 5
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
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
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
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
Reputation: 1
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
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
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
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
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
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
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
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
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
Reputation: 489
Here is an immutable solution if needed :
const newData = [
data.find(item => item === 'role'),
...data.filter(item => item !== 'role'),
],
Upvotes: 44
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
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
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
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
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
Reputation: 531
var data= ["email","role","type","name"];
if ("role" in data) data.splice(data.indexOf("role"),1); data.unshift("role");
data;
Upvotes: -4
Reputation: 319
var data= ["email","role","type","name"];
data.splice(data.indexOf("role"), 1);
data.unshift('role');
Upvotes: 3
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
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
Reputation: 664196
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..remove()
function you're using does take an index of an item.Upvotes: 2