Turn flat array into 2D array taking equally sized chunks - javascript

I'm working on a freecodecamp exercise, and need helping figuring out why my code is not working as intended. So the purpose of this exercise is to:
Write a function that splits an array (first argument) into groups the
length of size (second argument) and returns them as a
multidimensional array.
Why doesn't this code return [[0, 1], [2, 3], [4, 5]]?
function chunk(arr, size) {
// Break it up.
var output = [];
for (var x = 0; x < arr.length; x++) {
if (arr.length >= size) {
output.push(arr.splice([0], size));
} else {
output.push(arr.splice([0], arr.length));
}
}
return output;
}
document.write(JSON.stringify(
chunk([0, 1, 2, 3, 4, 5], 2)
));

Replace the for loop with a much simpler while loop:
function chunk(arr, size) {
// Break it up.
var output = [];
while (arr.length) {
if (arr.length >= size) {
output.push(arr.splice(0, size));
} else {
output.push(arr.splice(0, arr.length));
}
}
return output;
}
document.write(JSON.stringify(
chunk([0, 1, 2, 3, 4, 5], 2)
));

arr.splice also modifies arr. This way, in your example, after the first call of arr.splice(0, size) (note its not [0]), arr only has 4 elements, and after the second call, there are only 2 elements left. Therefore x < arr.legnth gets earlier true than you want it to be.
Another issue is that you only increase x by 1 in each iteration, where it should be size.
overall, this should work
function chunk(arr, size) {
// Break it up.
var output = [];
var l = arr.length;
for (var x = 0; x < l; x+=size) {
if (arr.length >= size) {
output.push(arr.splice(0, size));
} else {
output.push(arr.splice(0, arr.length));
}
}
return output;
}
console.log(chunk([0, 1, 2, 3, 4, 5], 2));

There is another way with modulo.
function chunk(arr, size) {
for (var x = 0, l = arr.length, arrD = []; x < l; x++) {
x % size === 0 ? arrD.push([arr[x]]) : arrD[arrD.length - 1].push(arr[x]);
}
return arrD;
}
var _ = chunk(["a", "b", "c", "d"], 2);
document.write(JSON.stringify(_));

Try this:
function chunk(arr, size) {
// Break it up.
var output = [];
var length = arr.length/size; // this is because each time you splice original array size reduces.
for (var x = 0; x < length; x++) {
if (arr.length > size) {
output.push(arr.splice([0], size));
} else {
output.push(arr.splice([0], arr.length));
}
}
return output;
}
console.log(chunk([0, 1, 2, 3, 4, 5], 2));

Your function isn't working because by using splice, you remove items from the array during the iteration, but you don't change the loop variable (x) accordingly.
A possible solution could be changing the for loop to while:
function chunk(arr, size) {
// Break it up.
var output = [];
while(arr.length > 0) {
if (arr.length >= size) {
output.push(arr.splice([0], size));
} else {
output.push(arr.splice([0], arr.length));
}
}
return output;
}

Try it this way:
function chunk(arr, size) {
// Break it up.
var output = [];
var yo = arr.length;
for (var x = 0; x < yo; x++) {
if (arr.length > size) {
output.push(arr.splice([0], size));
} else {
output.push(arr);
break;
}
}
return output;
}
console.log(chunk([0, 1, 2, 3, 4, 5], 2));
The issues were:
Array length was always changing as splice modifies the array itself.
arr.length > size should be the condition.
Fiddle

Another solution is:
function chunk(arr, size) {
var i, temparray, finalarray = [];
for (i=0; i<arr.length; i+=size) {
temparray = arr.slice(i, i+size);
finalarray.push(temparray);
}
return finalarray;
}

The problem
splice deletes elements from your array. Because of that, your array will automatically get smaller and smaller until no more elements are left.
This means your loop should stop when 0 < arr.length and only then. This also means you can completely remove your x variable.
Demo :
function chunk(arr, size) {
// Break it up.
var output = [];
for (; 0 < arr.length;) {
if (arr.length >= size) {
output.push(arr.splice(0, size));
} else {
output.push(arr.splice(0, arr.length));
}
}
return output;
}
var data = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M'];
document.body.innerHTML = '<pre>' +
'SIZE 3\n-------\n' +
JSON.stringify(chunk(data, 3), null, 2) +
'</pre>';
An alternative approach
It's better not to use splice, because it deletes all the data from your array. This means you can't eg. execute chunk twice or do anything else with your array after executing chunk.
A better approach, would be to use slice instead, which just copies a segment of your array and leaves the array intact.
Using slice, you can just use a classic for-loop, except that you increment isn't 1 but the size of your segments.
Demo :
function chunk(arr, size) {
var output = [];
var length = arr.length;
for (var x = 0, length = arr.length; x < length; x = x + size) {
output.push(arr.slice([x], x + size));
}
return output;
}
var data = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M'];
document.body.innerHTML =
'<pre>' +
'SIZE 2\n-------\n' +
JSON.stringify(chunk(data, 2), null, 2) +
"\n\n"+
'SIZE 3\n-------\n' +
JSON.stringify(chunk(data, 3), null, 2) +
"\n\n"+
'SIZE 4\n-------\n' +
JSON.stringify(chunk(data, 4), null, 2) +
"\n\n"+
'SIZE 5\n-------\n' +
JSON.stringify(chunk(data, 5), null, 2) +
"\n\n"+
'SIZE 6\n-------\n' +
JSON.stringify(chunk(data, 6), null, 2) +
"\n\n"+
'SIZE 7\n-------\n' +
JSON.stringify(chunk(data, 7), null, 2) +
'</pre>';

Here is a "one-liner" using recursion:
function chunk(a, s) {
return a.length ? [a.slice(0, s)].concat(chunk(a.slice(s), s)) : [];
}
document.write(JSON.stringify(
chunk(["a", "b", "c", "d"], 2)
));

Related

How can I return "1-5" if the array is [1,2,3,4,5] in JavaScript?

I am trying to make a program that returns ["1-5"] if I give [1,2,3,4,5].
I have made it but I can't filter it. So I want a code that will filter my output code. Or any code that is better than mine.
let array = [1,2,3,5,6,7,8,10,11, 34, 56,57,];
let x = [];
for(let i = 0; i < array.length; i++){
for(let j = 0; j < array.length; j++){
if(array[i] + j == array[j]){
x.push(array[i] + "-" + array[j]);
}
if(array[j] > array[i] + j && array[j + 1]){
let y = array.slice(j, array.length)
array = y;
i, j = 0;
}
if(array[i] - array[i + 1] != -1 && array[i + 1] - array[i] != 1 && array[i + 1] != undefined){
x.push(array[i]);
}
}
}
console.log(x);
The phrasing of the question makes this somewhat difficult to answer, but based on your code snippet I can gather that you are either:
Attempting to find the range of the entire array OR
Attempting to find contiguous ranges within the array
Based on these interpretations, you could answer this question as follows:
function detectRange(a) {
// clone a
const b = [...a]
// remove first value
const min = max = b.splice(0, 1)[0]
// compute range
const range = b.reduce(({min, max}, i) => {
if(i < min) min = i
if(i > max) max = i
return { min, max }
}, {min, max})
return range
}
function detectRanges(a) {
// clone a
const b = [...a]
// remove first value
const min = max = b.splice(0, 1)[0]
// init ranges array
const ranges = [ ]
// compute ranges
const range = b.reduce(({min, max}, i) => {
if(i === max + 1) {
return {min , max: i}
} else {
ranges.push({min, max})
return {min: i, max: i}
}
}, {min, max})
// push the remaining range onto the array
ranges.push(range)
return ranges
}
function printRange(r) {
console.log(`["${r.min}-${r.max}"]`)
}
function printRanges(r) {
r.forEach(i => {
printRange(i)
})
}
// detect and print range of whole array
printRange(detectRange([1, 2, 3, 5, 6, 7, 8, 10, 11, 34, 56, 57]))
// detect and print only contiguous ranges within array
printRanges(detectRanges([1, 2, 3, 5, 6, 7, 8, 10, 11, 34, 56, 57]))
If you assume that the list is sorted, we only need to traverse the list sequentially. There's no need to have double-nested loops. If you maintain sufficient states, you can determine whether you are in a group and you merely manage the start versus the last element in the group.
To simplify things I made use of ES6 string interpolation ${start}-${last}.
let array = [1,2,3,5,6,7,8,10,11, 34, 56,57];
let result = [ ];
let hasStart = false;
let start = 0;
let last = 0;
for (let num of array) {
if (!hasStart) {
hasStart = true;
last = start = num;
continue;
}
if (num === last + 1) {
last = num;
continue;
}
result.push( start === last ? start : `${start}-${last}` );
last = start = num;
}
if (hasStart) {
result.push( start === last ? start : `${start}-${last}` );
}
console.log(result);
Input: [1,2,3,4,5]
Output: ["1-5"]
So I assume you want to get string in format:
["smallestelement-largestelement"]
var input1 = [1,2,3,4,5]
console.log( "["+'"'+Math.min(...input1)+"-"+Math.max(...input1)+'"'+"]")
If what you want is string in format:
["firstelement-lastelement"]
var input1 = [1,2,3,4,5]
console.log( "["+'"'+input1[0]+"-"+input1.pop()+'"'+"]")
If you have an integer array, and if you want to output the range, you could natively sort() it (you can also provide rules for sorting) and use shift() for the first element and slice(-1) for the last:
let arr = [4,1,5,3].sort();
console.log(arr.shift()+'-'+arr.slice(-1));
As said in the comments, you should clarify if you wish "1-57" for the snippet array, or describe your use case more broadly.
const array = [1, 2, 3, 5, 6, 7, 8, 10, 11, 34, 56, 57];
let s = null;
const result = array.sort((a, b) => a - b).reduce((p, c, i, arr) => {
if (!s) s = c;
if (c + 1 !== arr[i + 1]) {
p.push(s === c ? s : `${s}-${c}`);
s = null;
}
return p
}, [])
console.log(result);

Given a list of n integers arr[0..(n-1)], determine the number of different pairs of elements within it which sum to k

I'm tackling this problem and I can't seem to arrive at the correct solution. The question is:
"Given a list of n integers arr[0..(n-1)], determine the number of different pairs of elements within it which sum to k. If an integer appears in the list multiple times, each copy is considered to be different; that is, two pairs are considered different if one pair includes at least one array index which the other doesn't, even if they include the same values.
My approach is that I'm building a map that contains each number in the array and the number of times it occurs. Then I iterate over the map to find my answer.
function numberOfWays(arr, k) {
let output = 0;
let map = {};
// put values and # of occurences into map
for(let i = 0; i < arr.length; i++) {
let key = arr[i];
if(!(key in map)) {
map[key] = 1;
} else {
map[key]++;
}
}
for(let key in map) {
let difference = k-key
if((difference) in map) {
if(k/2 === key) {
output += map[key]*(map[key]-1)/2;
} else {
output += map[key] * map[key] / 2; // divide by 2 so that pairs aren't counted twice
}
}
}
return output;
}
The two test cases are:
var arr_1 = [1, 2, 3, 4, 3]; expected result: [2] -- I'm getting [3]
var arr_2 = [1, 5, 3, 3, 3]; expected result: [4] -- I'm getting [5.5]
I'm definitely doing something wrong in my calculations, but I can't seem to wrap my ahead around it.
This is one way to nest the loops to find the pairs in array "arr" with the sum "k".
function numberOfWays(arr, k) {
let output = 0;
for (i = 0; i < arr.length; i++) {
for (n = i+1; n < arr.length; n++) {
if (arr[i] + arr[n] == k)
output++;
}
}
return output;
}
You could count the smaller and greater values for building k and then taker either the product or if only two of the same value is building the sum take factorial of the cound divided by two.
function numberOfWays(array, k) {
const
f = n => +!n || n * f(n - 1),
pairs = {};
for (const value of array) {
const smaller = Math.min(value, k - value);
pairs[smaller] ??= { one: 2 * smaller === k, min: 0, max: 0 };
pairs[smaller][value === smaller ? 'min' : 'max']++;
}
let count = 0;
for (const k in pairs) {
const { one, min, max } = pairs[k];
if (one) {
if (min > 1) count += f(min) / 2;
} else if (min && max) {
count += min * max;
}
}
return count;
}
console.log(numberOfWays([1, 2, 3, 4, 3], 6)); // 2
console.log(numberOfWays([1, 5, 3, 3, 3], 6)); // 4
function numberOfWays(items, k) {
// Clone as to not mutate original array
const arr = [...items]
let count = 0
// Stop comparing when no items left to compare
while (arr.length) {
for (let i = 0; i < arr.length; i++) {
// Compare each item to the first item
const sum = arr[0] + arr[i + 1]
if (sum === k) {
count++
}
}
// Remove the first item after comparing to the others
arr.shift()
}
return count
}
console.log(numberOfWays([1, 2, 3, 4, 3], 6))
console.log(numberOfWays([1, 5, 3, 3, 3], 6))
console.log(numberOfWays([1, 1, 1, 1, 1], 2))
import math
from math import factorial as f
def get_number_of_combination(n,r):
return f(n)//(f(n-r)*f(r))
def numberOfWays(arr, k):
num_count = {}
num_ways = 0
for i in arr:
old_count = num_count.get(i,0)
num_count.update({i: old_count+1})
for i in list(num_count.keys()):
if i == k - i and num_count.get(i,0) > 1:
num_ways += (get_number_of_combination(num_count.get(i,0),2))
num_count.update({i:0})
else:
i_n = num_count.get(i, 0)
ki_n = num_count.get(k-i, 0)
num_ways += i_n * ki_n
num_count.update({i:0,k-i:0})
return num_ways

Splitting array into equal halves

Problem: find an index N where the sum of the integers to the left of N is equal to the sum of the integers to the right of N. If there is no index that would make this happen, return -1.
My solution
function findEvenIndex(arr) {
var sum = i => i.reduce((a, b) => a + b),
l = arr.length;
for (let j = 0; j <= l; j++) {
if (sum(arr.slice(0, j - 1)) === sum(arr.slice(j, l))) {
return j
} else {
continue;
}
}
return -1
}
console.log(
findEvenIndex([1, 2, 3, 4, 3, 2, 1])
)
When I run this on say findEvenIndex([1,2,3,4,3,2,1]), It doesn't return anything? Where is the error that prevents 3 from being returned in the case of this example?
I've set the for loop procedure as follows to see what's going on
for(let j = 0; j <= arr.length; j++){
var left = arr.slice(0, j-1), right = arr.slice(j)
console.log(left, right)
}
/* returns
[1] [3,4,3,2,1]
[1,2] [4,3,2,1]
[1,2,3] [3,2,1]
as expected
*/
However, when try to console.log the sum of these arrays:
function sum(i){ return i.reduce((a, b) => a+b)}
var l = arr.length;
for(let j = 0; j <= l; j++){
var left = arr.slice(0, j-1), right = arr.slice(j)
console.log(sum(left), sum(right))
}
Using the snippet above, findEvenIndex([1,2,3,4,3,2,1]) returns "15 16"?
The main issue with your code is that calling sum([]) throws an error (which you will find in the console during debugging):
Reduce of empty array with no initial value
The reduce method does not know what to return if your array doesn't have any values. You solve it by passing the initial value as a second argument to .reduce:
const add = (a, b) => a + b;
[1, 2, 3].reduce(add); // add(add(1, 2), 3)
[1, 2].reduce(add); // add(1, 2)
[1].reduce(add); // 1
[].reduce(add); // ERROR: Reduce of empty array
// with no initial value
[1, 2].reduce(add, 0); // add(add(0, 1), 2)
[1].reduce(add, 0); // add(0, 1)
[].reduce(add, 0); // 0
Once you fix that, it's easier to debug the rest of the code.
Fixing it
Here's an example that I think does what it should do:
function findEvenIndex(arr) {
// Add a seed value --v
var sum = i => i.reduce((a, b) => a + b, 0),
l = arr.length;
for (let j = 0; j <= l; j++) {
const left = arr.slice(0, j);
const right = arr.slice(j + 1);
const leftSum = sum(left);
const rightSum = sum(right);
console.log(
{ left, right, leftSum, rightSum }
);
if (leftSum === rightSum) {
return j
}
}
return -1
}
console.log(
findEvenIndex([1]), // 0
findEvenIndex([1, 2, 3, 4, 3, 2, 1]), // 3
findEvenIndex([10, 0, 5, 5]), // 1
findEvenIndex([3, 2, 1]) // -1
)
Another approach
Note that looping over all elements of the array for every index is quite expensive! A more efficient approach would be:
Take the sum of the source array, store it as rightSum
Define leftSum as 0
Look at the integer value at index 0 and subtract it from rightSum
If leftSum === rightSum, return 0
Else, add value to leftSum and increment index
Once you've reached the final index, return -1
const findEvenIndex = (arr) => {
let leftSum = 0;
let rightSum = arr
.reduce((a, b) => a + b, 0);
for (let i = 0; i < arr.length; i += 1) {
const n = arr[i];
rightSum -= n;
if (leftSum === rightSum) return i;
leftSum += n;
}
return -1;
}
console.log(
findEvenIndex([1]), // 0
findEvenIndex([1, 2, 3, 4, 3, 2, 1]), // 3
findEvenIndex([10, 0, 5, 5]), // 1
findEvenIndex([3, 2, 1]) // -1
)
You can get the index like following using reduce(). Your implementation regarding reduce() is not correct.
function findEvenIndex(arr)
{
for(let i = 0; i < arr.length; i++) {
let leftSum = arr.slice(0, i).reduce((accumulator, current) => accumulator + current, 0);
let rightSum = arr.slice(i + 1).reduce((accumulator, current) => accumulator + current, 0);
if (leftSum === rightSum) {
return i;
}
}
return -1;
}
console.log(
findEvenIndex([1, 2, 3, 4, 3, 2, 1])
)
Please check following blog to find out how Array reduce() work
https://www.javascripttutorial.net/javascript-array-reduce/
Upon finishing my solution, I noticed that it's effectively the same as #Abu's answer above. The idea is to brute force the way through the array, comparing the two halves as you go.
/*
Find an index N where the sum of the integers to the left of N is equal to the sum of the integers to the right of N. If there is no index that would make this happen, return -1
*/
const array = [10, 90, 10, 1, 10, 90, 10];
// incrementTotal :: (Number t, Number n) -> t
incrementTotal = (total, number) => total + number;
// indexIsEqual :: (Array a, Number c) -> Boolean
function indexIsEqual(array, count) {
let chunkL = array.slice(0, count-1);
let chunkR = array.slice(count , );
return chunkL.reduce(incrementTotal) === chunkR.reduce(incrementTotal);
}
// findEvenIndex :: (Array a) -> (a[x] || -1)
function findEvenIndex(array) {
for (let count = 2; count < array.length; count++) {
if (indexIsEqual(array, count)) {
return array[count-1];
}
}
return -1;
}
console.log(findEvenIndex(array));

How would I write a function that returns all combinations of 3 numbers that equal the target sum WITH RECURSION

What I have so far
let threeSum = (array, targetSum) => {
let newArray = []
if (array.length === 0) {
return newArray
}
for (let i = 0; i < array.length; i++) {
const num1 = array[i];
for (let j = i + 1; j < array.length; j++) {
const num2 = array[j];
for (let k = j + 1; k < array.length; k++) {
const num3 = array[k];
if (num1 + num2 + num3 === targetSum) {
newArray.push([num1, num2, num3])
}
}
}
}
return newArray
}
console.log(threeSum([12, 3, 1, 2, -6, 5, -8, 6], 0))
// [[-8, 2, 6], [-8, 3, 5], [-6, 1, 5]]
This works but only because I'm iterating through the array, I've tried checking the last and first number then slicing the array everytime I recall the function. I've written a base case so far but I'm lost on the recursion step.
This is what I have for the recursion:
let threeSum = (array, targetSum) => {
let newArray = []
if (array.length === 0) {
return newArray
}
let num1 = array[0]
let num2 = array[1]
let num3 = array[2]
if (num1 + num2 + num3 === targetSum) {
newArray.push([num1, num2, num3])
}
return array.slice(1), targetSum
}
console.log(threeSum([12, 3, 1, 2, -6, 5, -8, 6], 0))
// [[-8, 2, 6], [-8, 3, 5], [-6, 1, 5]]
Generators make recursion a bit easier because you can just yield the results. Here the base case is a set of three numbers and a total that equals the values passed in (or no more numbers).
Then just take the first number, add it to found, use it to adjust the total and found numbers and recurse with the rest:
function* threeSum(a, total, found = []) {
if (found.length == 3) {
if (total == 0) yield found
return
}
if (a.length == 0) return
yield* threeSum(a.slice(1), total - a[0], [...found, a[0]])
yield* threeSum(a.slice(1), total, found)
}
console.log([...threeSum([12, 3, 1, 2, -6, 5, -8, 6], 0)])
As pointed out, recursion may not be the best approach, but if you must use it, ...
The idea here is at each level of recursion, you make the problem smaller (by using one of the numbers), and then recurse on the 'rest of the array'.
You stop after 3 numbers are used or you go over the target.
This is called Backtracking.
<div id=divvie> </div>
<script>
var html = "";
var target = 23; // or pass it down
function threesum(subsum, sumsofar, n, Arr, istart)
{
var i;
if (n == 0)
{
if (sumsofar == target)
html += subsum + "<br>";
return;
}
for (i = istart; i < Arr.length; i++)
{
var j = Arr[i];
if (sumsofar + j <= target)
threesum(subsum + "," + j, (sumsofar+j), n-1, Arr, i+1);
}
}
threesum(target + " = ", 0, 3, [8, 5, 2, 6, 9, 7, 4, 1, 12, 3], 0);
document.getElementById("divvie").innerHTML = html;
</script>
Output is:
23 = ,8,6,9
23 = ,8,12,3
23 = ,5,6,12
23 = ,2,9,12
23 = ,7,4,12

Move an array element from one array position to another

I'm having a hard time figuring out how to move an element of an array. For example, given the following:
var array = [ 'a', 'b', 'c', 'd', 'e'];
How can I write a function to move the element 'd' to the left of 'b' ?
Or 'a' to the right of 'c'?
After moving the elements, the indexes of the rest of the elements should be updated. The resulting array would be:
array = ['a', 'd', 'b', 'c', 'e']
This seems like it should be pretty simple, but I can't wrap my head around it.
If you'd like a version on npm, array-move is the closest to this answer, although it's not the same implementation. See its usage section for more details. The previous version of this answer (that modified Array.prototype.move) can be found on npm at array.prototype.move.
I had fairly good success with this function:
function array_move(arr, old_index, new_index) {
if (new_index >= arr.length) {
var k = new_index - arr.length + 1;
while (k--) {
arr.push(undefined);
}
}
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr; // for testing
};
// returns [2, 1, 3]
console.log(array_move([1, 2, 3], 0, 1));
Note that the last return is simply for testing purposes: splice performs operations on the array in-place, so a return is not necessary. By extension, this move is an in-place operation. If you want to avoid that and return a copy, use slice.
Stepping through the code:
If new_index is greater than the length of the array, we want (I presume) to pad the array properly with new undefineds. This little snippet handles this by pushing undefined on the array until we have the proper length.
Then, in arr.splice(old_index, 1)[0], we splice out the old element. splice returns the element that was spliced out, but it's in an array. In our above example, this was [1]. So we take the first index of that array to get the raw 1 there.
Then we use splice to insert this element in the new_index's place. Since we padded the array above if new_index > arr.length, it will probably appear in the right place, unless they've done something strange like pass in a negative number.
A fancier version to account for negative indices:
function array_move(arr, old_index, new_index) {
while (old_index < 0) {
old_index += arr.length;
}
while (new_index < 0) {
new_index += arr.length;
}
if (new_index >= arr.length) {
var k = new_index - arr.length + 1;
while (k--) {
arr.push(undefined);
}
}
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr; // for testing purposes
};
// returns [1, 3, 2]
console.log(array_move([1, 2, 3], -1, -2));
Which should account for things like array_move([1, 2, 3], -1, -2) properly (move the last element to the second to last place). Result for that should be [1, 3, 2].
Either way, in your original question, you would do array_move(arr, 0, 2) for a after c. For d before b, you would do array_move(arr, 3, 1).
I like this way. It's concise and it works.
function arraymove(arr, fromIndex, toIndex) {
var element = arr[fromIndex];
arr.splice(fromIndex, 1);
arr.splice(toIndex, 0, element);
}
Note: always remember to check your array bounds.
Run Snippet in jsFiddle
Here's a one liner I found on JSPerf....
Array.prototype.move = function(from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
};
which is awesome to read, but if you want performance (in small data sets) try...
Array.prototype.move2 = function(pos1, pos2) {
// local variables
var i, tmp;
// cast input parameters to integers
pos1 = parseInt(pos1, 10);
pos2 = parseInt(pos2, 10);
// if positions are different and inside array
if (pos1 !== pos2 && 0 <= pos1 && pos1 <= this.length && 0 <= pos2 && pos2 <= this.length) {
// save element from position 1
tmp = this[pos1];
// move element down and shift other elements up
if (pos1 < pos2) {
for (i = pos1; i < pos2; i++) {
this[i] = this[i + 1];
}
}
// move element up and shift other elements down
else {
for (i = pos1; i > pos2; i--) {
this[i] = this[i - 1];
}
}
// put element from position 1 to destination
this[pos2] = tmp;
}
}
I can't take any credit, it should all go to Richard Scarrott. It beats the splice based method for smaller data sets in this performance test. It is however significantly slower on larger data sets as Darwayne points out.
The splice() method adds/removes items to/from an array, and returns the removed item(s).
Note: This method changes the original array. /w3schools/
Array.prototype.move = function(from,to){
this.splice(to,0,this.splice(from,1)[0]);
return this;
};
var arr = [ 'a', 'b', 'c', 'd', 'e'];
arr.move(3,1);//["a", "d", "b", "c", "e"]
var arr = [ 'a', 'b', 'c', 'd', 'e'];
arr.move(0,2);//["b", "c", "a", "d", "e"]
as the function is chainable this works too:
alert(arr.move(0,2).join(','));
demo here
My 2c. Easy to read, it works, it's fast, it doesn't create new arrays.
function move(array, from, to) {
if( to === from ) return array;
var target = array[from];
var increment = to < from ? -1 : 1;
for(var k = from; k != to; k += increment){
array[k] = array[k + increment];
}
array[to] = target;
return array;
}
Here is my one liner ES6 solution with an optional parameter on.
if (typeof Array.prototype.move === "undefined") {
Array.prototype.move = function(from, to, on = 1) {
this.splice(to, 0, ...this.splice(from, on))
}
}
Adaptation of the first solution proposed by digiguru
The parameter on is the number of element starting from from you want to move.
Here is a chainable variation of this:
if (typeof Array.prototype.move === "undefined") {
Array.prototype.move = function(from, to, on = 1) {
return this.splice(to, 0, ...this.splice(from, on)), this
}
}
[3, 4, 5, 1, 2].move(3, 0, 2) // => [1, 2, 3, 4, 5]
If you'd like to avoid prototype pollution, here's a stand-alone function:
function move(array, from, to, on = 1) {
return array.splice(to, 0, ...array.splice(from, on)), array
}
move([3, 4, 5, 1, 2], 3, 0, 2) // => [1, 2, 3, 4, 5]
And finally, here's a pure function that doesn't mutate the original array:
function moved(array, from, to, on = 1) {
return array = array.slice(), array.splice(to, 0, ...array.splice(from, on)), array
}
This should cover basically every variation seen in every other answer.
Got this idea from #Reid of pushing something in the place of the item that is supposed to be moved to keep the array size constant. That does simplify calculations. Also, pushing an empty object has the added benefits of being able to search for it uniquely later on. This works because two objects are not equal until they are referring to the same object.
({}) == ({}); // false
So here's the function which takes in the source array, and the source, destination indexes. You could add it to the Array.prototype if needed.
function moveObjectAtIndex(array, sourceIndex, destIndex) {
var placeholder = {};
// remove the object from its initial position and
// plant the placeholder object in its place to
// keep the array length constant
var objectToMove = array.splice(sourceIndex, 1, placeholder)[0];
// place the object in the desired position
array.splice(destIndex, 0, objectToMove);
// take out the temporary object
array.splice(array.indexOf(placeholder), 1);
}
This is based on #Reid's solution. Except:
I'm not changing the Array prototype.
Moving an item out of bounds to the right does not create undefined items, it just moves the item to the right-most position.
Function:
function move(array, oldIndex, newIndex) {
if (newIndex >= array.length) {
newIndex = array.length - 1;
}
array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
return array;
}
Unit tests:
describe('ArrayHelper', function () {
it('Move right', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 0, 1);
assert.equal(array[0], 2);
assert.equal(array[1], 1);
assert.equal(array[2], 3);
})
it('Move left', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 1, 0);
assert.equal(array[0], 2);
assert.equal(array[1], 1);
assert.equal(array[2], 3);
});
it('Move out of bounds to the left', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 1, -2);
assert.equal(array[0], 2);
assert.equal(array[1], 1);
assert.equal(array[2], 3);
});
it('Move out of bounds to the right', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 1, 4);
assert.equal(array[0], 1);
assert.equal(array[1], 3);
assert.equal(array[2], 2);
});
});
You can implement some basic calculus and create a universal function for moving array elements from one position to the other.
For JavaScript it looks like this:
function magicFunction (targetArray, indexFrom, indexTo) {
targetElement = targetArray[indexFrom];
magicIncrement = (indexTo - indexFrom) / Math.abs (indexTo - indexFrom);
for (Element = indexFrom; Element != indexTo; Element += magicIncrement){
targetArray[Element] = targetArray[Element + magicIncrement];
}
targetArray[indexTo] = targetElement;
}
Check out "moving array elements" at "Gloommatter" for detailed explanation.
https://web.archive.org/web/20121105042534/http://www.gloommatter.com:80/DDesign/programming/moving-any-array-elements-universal-function.html
I've implemented an immutable ECMAScript 6 solution based off of #Merc's answer over here:
const moveItemInArrayFromIndexToIndex = (array, fromIndex, toIndex) => {
if (fromIndex === toIndex) return array;
const newArray = [...array];
const target = newArray[fromIndex];
const inc = toIndex < fromIndex ? -1 : 1;
for (let i = fromIndex; i !== toIndex; i += inc) {
newArray[i] = newArray[i + inc];
}
newArray[toIndex] = target;
return newArray;
};
The variable names can be shortened, just used long ones so that the code can explain itself.
One approach would be to create a new array with the pieces in the order you want, using the slice method.
Example
var arr = [ 'a', 'b', 'c', 'd', 'e'];
var arr2 = arr.slice(0,1).concat( ['d'] ).concat( arr.slice(2,4) ).concat( arr.slice(4) );
arr.slice(0,1) gives you ['a']
arr.slice(2,4) gives you ['b', 'c']
arr.slice(4) gives you ['e']
Another pure JS variant using ES6 array spread operator with no mutation
const reorder = (array, sourceIndex, destinationIndex) => {
const smallerIndex = Math.min(sourceIndex, destinationIndex);
const largerIndex = Math.max(sourceIndex, destinationIndex);
return [
...array.slice(0, smallerIndex),
...(sourceIndex < destinationIndex
? array.slice(smallerIndex + 1, largerIndex + 1)
: []),
array[sourceIndex],
...(sourceIndex > destinationIndex
? array.slice(smallerIndex, largerIndex)
: []),
...array.slice(largerIndex + 1),
];
}
// returns ['a', 'c', 'd', 'e', 'b', 'f']
console.log(reorder(['a', 'b', 'c', 'd', 'e', 'f'], 1, 4))
The splice method of Array might help: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice
Just keep in mind it might be relatively expensive since it has to actively re-index the array.
I needed an immutable move method (one that didn't change the original array), so I adapted #Reid's accepted answer to simply use Object.assign to create a copy of the array before doing the splice.
Array.prototype.immutableMove = function (old_index, new_index) {
var copy = Object.assign([], this);
if (new_index >= copy.length) {
var k = new_index - copy.length;
while ((k--) + 1) {
copy.push(undefined);
}
}
copy.splice(new_index, 0, copy.splice(old_index, 1)[0]);
return copy;
};
Here is a jsfiddle showing it in action.
Array.prototype.moveUp = function (value, by) {
var index = this.indexOf(value),
newPos = index - (by || 1);
if (index === -1)
throw new Error("Element not found in array");
if (newPos < 0)
newPos = 0;
this.splice(index, 1);
this.splice(newPos, 0, value);
};
Array.prototype.moveDown = function (value, by) {
var index = this.indexOf(value),
newPos = index + (by || 1);
if (index === -1)
throw new Error("Element not found in array");
if (newPos >= this.length)
newPos = this.length;
this.splice(index, 1);
this.splice(newPos, 0, value);
};
var arr = ['banana', 'curyWurst', 'pc', 'remembaHaruMembaru'];
alert('withiout changes= '+arr[0]+' ||| '+arr[1]+' ||| '+arr[2]+' ||| '+arr[3]);
arr.moveDown(arr[2]);
alert('third word moved down= '+arr[0] + ' ||| ' + arr[1] + ' ||| ' + arr[2] + ' ||| ' + arr[3]);
arr.moveUp(arr[2]);
alert('third word moved up= '+arr[0] + ' ||| ' + arr[1] + ' ||| ' + arr[2] + ' ||| ' + arr[3]);
http://plnkr.co/edit/JaiAaO7FQcdPGPY6G337?p=preview
Here's one way to do it in an immutable way. It handles negative numbers as well as an added bonus. This is reduces number of possible bugs at the cost of performance compared to editing the original array.
const numbers = [1, 2, 3];
const moveElement = (array, from, to) => {
const copy = [...array];
const valueToMove = copy.splice(from, 1)[0];
copy.splice(to, 0, valueToMove);
return copy;
};
console.log(moveElement(numbers, 0, 2))
// > [2, 3, 1]
console.log(moveElement(numbers, -1, -3))
// > [3, 1, 2]
One approach would be to use splice() to remove the item from the array and then by using the splice() method once again, insert the removed item into the target index.
const array = ['a', 'b', 'c', 'd', 'e']
const newArray = moveItem(array, 3, 1) // move element from index 3 to index 1
function moveItem(arr, fromIndex, toIndex){
let itemRemoved = arr.splice(fromIndex, 1) // assign the removed item as an array
arr.splice(toIndex, 0, itemRemoved[0]) // insert itemRemoved into the target index
return arr
}
console.log(newArray)
In 2022, this typescript utility will work along with a unit test.
export const arrayMove = <T>(arr: T[], fromIndex: number, toIndex: number) => {
const newArr = [...arr];
newArr.splice(toIndex, 0, newArr.splice(fromIndex, 1)[0]);
return newArr;
};
const testArray = ['1', '2', '3', '4'];
describe('arrayMove', () => {
it('should move array item to toIndex', () => {
expect(arrayMove(testArray, 2, 0)).toEqual(['3', '1', '2', '4']);
expect(arrayMove(testArray, 3, 1)).toEqual(['1', '4', '2', '3']);
expect(arrayMove(testArray, 1, 2)).toEqual(['1', '3', '2', '4']);
expect(arrayMove(testArray, 0, 2)).toEqual(['2', '3', '1', '4']);
});
});
I love immutable, functional one liners :) ...
const swapIndex = (array, from, to) => (
from < to
? [...array.slice(0, from), ...array.slice(from + 1, to + 1), array[from], ...array.slice(to + 1)]
: [...array.slice(0, to), array[from], ...array.slice(to, from), ...array.slice(from + 1)]
);
Find and move an element from "n"th position to 0th position.
Eg: Find and move 'd' to 0th position:
let arr = [ 'a', 'b', 'c', 'd', 'e'];
arr = [...arr.filter(item => item === 'd'), ...arr.filter(item => item !== 'd')];
It is stated in many places (adding custom functions into Array.prototype) playing with the Array prototype could be a bad idea, anyway I combined the best from various posts, I came with this, using modern Javascript:
Object.defineProperty(Array.prototype, 'immutableMove', {
enumerable: false,
value: function (old_index, new_index) {
var copy = Object.assign([], this)
if (new_index >= copy.length) {
var k = new_index - copy.length;
while ((k--) + 1) { copy.push(undefined); }
}
copy.splice(new_index, 0, copy.splice(old_index, 1)[0]);
return copy
}
});
//how to use it
myArray=[0, 1, 2, 3, 4];
myArray=myArray.immutableMove(2, 4);
console.log(myArray);
//result: 0, 1, 3, 4, 2
Hope can be useful to anyone
This version isn't ideal for all purposes, and not everyone likes comma expressions, but here's a one-liner that's a pure expression, creating a fresh copy:
const move = (from, to, ...a) => (a.splice(to, 0, ...a.splice(from, 1)), a)
A slightly performance-improved version returns the input array if no move is needed, it's still OK for immutable use, as the array won't change, and it's still a pure expression:
const move = (from, to, ...a) =>
from === to
? a
: (a.splice(to, 0, ...a.splice(from, 1)), a)
The invocation of either is
const shuffled = move(fromIndex, toIndex, ...list)
i.e. it relies on spreading to generate a fresh copy. Using a fixed arity 3 move would jeopardize either the single expression property, or the non-destructive nature, or the performance benefit of splice. Again, it's more of an example that meets some criteria than a suggestion for production use.
const move = (from, to, ...a) =>from === to ? a : (a.splice(to, 0, ...a.splice(from, 1)), a);
const moved = move(0, 2, ...['a', 'b', 'c']);
console.log(moved)
I thought this was a swap problem but it's not. Here's my one-liner solution:
const move = (arr, from, to) => arr.map((item, i) => i === to ? arr[from] : (i >= Math.min(from, to) && i <= Math.max(from, to) ? arr[i + Math.sign(to - from)] : item));
Here's a small test:
let test = ['a', 'b', 'c', 'd', 'e'];
console.log(move(test, 0, 2)); // [ 'b', 'c', 'a', 'd', 'e' ]
console.log(move(test, 1, 3)); // [ 'a', 'c', 'd', 'b', 'e' ]
console.log(move(test, 2, 4)); // [ 'a', 'b', 'd', 'e', 'c' ]
console.log(move(test, 2, 0)); // [ 'c', 'a', 'b', 'd', 'e' ]
console.log(move(test, 3, 1)); // [ 'a', 'd', 'b', 'c', 'e' ]
console.log(move(test, 4, 2)); // [ 'a', 'b', 'e', 'c', 'd' ]
console.log(move(test, 4, 0)); // [ 'e', 'a', 'b', 'c', 'd' ]
This is a really simple method using splice
Array.prototype.moveToStart = function(index) {
this.splice(0, 0, this.splice(index, 1)[0]);
return this;
};
I ended up combining two of these to work a little better when moving both small and large distances. I get fairly consistent results, but this could probably be tweaked a little bit by someone smarter than me to work differently for different sizes, etc.
Using some of the other methods when moving objects small distances was significantly faster (x10) than using splice. This might change depending on the array lengths though, but it is true for large arrays.
function ArrayMove(array, from, to) {
if ( Math.abs(from - to) > 60) {
array.splice(to, 0, array.splice(from, 1)[0]);
} else {
// works better when we are not moving things very far
var target = array[from];
var inc = (to - from) / Math.abs(to - from);
var current = from;
for (; current != to; current += inc) {
array[current] = array[current + inc];
}
array[to] = target;
}
}
https://web.archive.org/web/20181026015711/https://jsperf.com/arraymove-many-sizes
TypeScript Version
Copied from #Merc's answer. I like that one best because it is not creating new arrays and modifies the array in place. All I did was update to ES6 and add the types.
export function moveItemInArray<T>(workArray: T[], fromIndex: number, toIndex: number): T[] {
if (toIndex === fromIndex) {
return workArray;
}
const target = workArray[fromIndex];
const increment = toIndex < fromIndex ? -1 : 1;
for (let k = fromIndex; k !== toIndex; k += increment) {
workArray[k] = workArray[k + increment];
}
workArray[toIndex] = target;
return workArray;
}
Array.move.js
Summary
Moves elements within an array, returning an array containing the moved elements.
Syntax
array.move(index, howMany, toIndex);
Parameters
index: Index at which to move elements. If negative, index will start from the end.
howMany: Number of elements to move from index.
toIndex: Index of the array at which to place the moved elements. If negative, toIndex will start from the end.
Usage
array = ["a", "b", "c", "d", "e", "f", "g"];
array.move(3, 2, 1); // returns ["d","e"]
array; // returns ["a", "d", "e", "b", "c", "f", "g"]
Polyfill
Array.prototype.move || Object.defineProperty(Array.prototype, "move", {
value: function (index, howMany, toIndex) {
var
array = this,
index = parseInt(index) || 0,
index = index < 0 ? array.length + index : index,
toIndex = parseInt(toIndex) || 0,
toIndex = toIndex < 0 ? array.length + toIndex : toIndex,
toIndex = toIndex <= index ? toIndex : toIndex <= index + howMany ? index : toIndex - howMany,
moved;
array.splice.apply(array, [toIndex, 0].concat(moved = array.splice(index, howMany)));
return moved;
}
});
I used the nice answer of #Reid, but struggled with moving an element from the end of an array one step further - to the beginning (like in a loop).
E.g. ['a', 'b', 'c'] should become ['c', 'a', 'b'] by calling .move(2,3)
I achieved this by changing the case for new_index >= this.length.
Array.prototype.move = function (old_index, new_index) {
console.log(old_index + " " + new_index);
while (old_index < 0) {
old_index += this.length;
}
while (new_index < 0) {
new_index += this.length;
}
if (new_index >= this.length) {
new_index = new_index % this.length;
}
this.splice(new_index, 0, this.splice(old_index, 1)[0]);
return this; // for testing purposes
};
As an addition to Reid's excellent answer (and because I cannot comment);
You can use modulo to make both negative indices and too large indices "roll over":
function array_move(arr, old_index, new_index) {
new_index =((new_index % arr.length) + arr.length) % arr.length;
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr; // for testing
}
// returns [2, 1, 3]
console.log(array_move([1, 2, 3], 0, 1));

Categories

Resources