I have multiple arrays of indexes, representing an order:
[[0,1,2], [2,0,1], [1,2,0], [0,1,2], ...]
I need to construct a new array of indexes with a length equal to the total number of indexes in the input while ordering the values using the position indicated in each array in the input.
This would be the output of the input above:
[0, 1, 2, 5, 3, 4, 7, 8, 6, 9, 10, 11, ...]
The total length is 12, so the list of indexes will contain 0-11
The first array of the input is 0, 1, 2 so the output starts with 0, 1, 2
The second array of the input is 2, 0, 1 and the next 3 indexes in the new list are 3-5. ordering these using the second array of the input results in 5,3,4
And so on...
You could use Array#reduce, Array#forEach and the length of the actual array for the count. Then push the sum of all length until now and the value of inner array to the result set.
var array = [[0, 1, 2], [2, 0, 1], [1, 2, 0], [0, 1, 2]],
result = [];
array.reduce(function (r, a) {
a.forEach(function (b) {
result.push(r + b);
});
return r + a.length;
}, 0);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I'm confused by your example, but it sounds like you want to flatten your array by one level. You can do so like this:
var arrays = [[0,1,2], [2,0,1], [1,2,0], [0,1,2]];
var result = [].concat.apply([], arrays);
console.log(result);
If you're using a library like Underscore, there are built-in methods to do that, like the flatten method.
The input and output examples are confusing, but I think what you want it this:
var array = [...] // your array
var result = Array.prototype.concat.apply([], array);
I believe that I interpreted your question based on the expected output, and edited the question accordingly.
You'll need to loop over the sub-arrays in the input and get the next set of indexes for the output, then add those to the output using the order of the sub-array.
This seems to do the trick:
var input = [[0, 1, 2], [2, 0, 1], [1, 2, 0], [0, 1, 2]];
var output = [];
// add values to the output for each of the inputs
$.each(input, function (index, value) {
// get the next and last values to add
var startIndex = output.length;
var lastIndex = (startIndex + value.length) - 1;
// make an array of sequential values from startIndex to lastIndex
var arrayToAdd = [];
for (var i = startIndex; i <= lastIndex; i++) {
arrayToAdd.push(i);
}
// add the values from arrayToAdd in the order of the indexes in the input
$.each(value, function (innerindex, innerindexvalue) {
output.push(arrayToAdd[innerindexvalue]);
});
});
console.log(output);
Related
I have an array like:
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
How I can reorder this array with the following rules:
myArray[0][0] to reduce size to 2 elements (values 1,2 stay, 3,4 goes to next array)
keep all values just move extra array elements to the next array, but all arrays need to keep the current number of elements except last
WHat I already try is:
function conditionalChunk(array, size, rules = {}) {
let copy = [...array],
output = [],
i = 0;
while (copy.length)
output.push( copy.splice(0, rules[i++] ?? size) )
return output
}
conditionalChunk(myArray, 3, {0:2});
but in that case, I need to put rules for all arrays in the array, I need to know a number of elements for all arrays in the array, and that's what I want to avoid.
Is there any elegant way to do that?
The requirements were not that clear and I don't know why you would do any of that but here you have your solution: I have written a function that you can use to limit the cardinality of the subArrays.
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
function reorderArray(arr) {
let buff = [];
let newArr = [];
let maxSubarrCardinality = 2;
//flatMapping second level elements
//in a single level buffer
for (subArr of arr) {
for (elem of subArr) {
buff.push(elem);
}
}
//Inserting elements one at the time
//into a new array
for (elem in buff) {
//when the new array is empty
//push in the new array the first empty subArray
if (newArr.at(-1) == undefined)
newArr.push([]);
//if the last subArray has reached
//the maxCardinality push a new subArray
else if (newArr.at(-1).length >= maxSubarrCardinality) {
newArr.push([]);
newArr.at(-1).push(elem);
}
//if none of the previous special cases
//just add the element to the last subArray of the newArray
else {
newArr.at(-1).push(elem);
}
}
return newArr;
}
myArray = reorderArray(myArray);
Steps
I used Array#flat, so I had to keep track of the indices and length of each item.
let i = 0;
let itemLength = array[0]?.length;
After flattening the array, I use Array#reduce to loop through the items, setting the initialValue to an empty array.
I get the last item in the array and check if its length has reached the maximum allowed (which should be the one set in the rules argument for that index or the size argument).
If the max hasn't been reached, I push the current item into the last array. If it has, I create a new array, with the item as the only element
array.flat().reduce((acc, cur) => {
if (acc.length === 0) acc.push([]); // Just for the first iteration
if (acc.at(-1).length < (rules[i] ?? size)) acc[acc.length - 1].push(cur);
else acc.push([cur]);
If then either decrement the value of itemLength, or set it to the length of the next array. and increment the i variable to the next index
itemLength = itemLength === 0 ? (array[++i] ?? []).length : --itemLength;
let array = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
function conditionalChunk(array, size, rules = {}) {
let i = 0;
let itemLength = array[0]?.length;
return array.flat().reduce((acc, cur) => {
if (acc.length === 0) acc.push([]); // Just for the first iteration
if (acc.at(-1).length < (rules[i] ?? size)) acc[acc.length - 1].push(cur);
else acc.push([cur])
itemLength = itemLength === 0 ? (array[++i] ?? []).length : --itemLength;
return acc;
}, []);
}
console.log(JSON.stringify(conditionalChunk(array, 3, { 0: 2 })));
Here is what I came up with. Paste the code into the chrome console and test it.
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
//Getting initial lengths of inner arrays [4, 2, 3, 1]
var lengths = [];
myArray.forEach((arr) => {lengths.push(arr.length);});
// Extracting the elements of all the inner arrays into one array.
var allElements = [].concat.apply([], myArray);
// Updating the lengths of first and last inner arrays based on your requirement.
var firstArrLen = 2;
var lastArrLen = lengths[lengths.length -1] + (lengths[0] - 2)
lengths[0] = firstArrLen;
lengths[lengths.length -1] = lastArrLen;
// Initializing the final/result array.
var finalArr = [];
// Adding/Pushing the new inner arrays into the finalArr
for(var len of lengths) {
var tempArr = [];
for(var i=0; i<len; i++) {
tempArr.push(allElements[i]);
}
finalArr.push(tempArr);
for(var i=0; i<len; i++) {
// removes the first element from the array.
allElements.shift();
}
}
console.log(finalArr);
Given an array of integers, where the values should be sorted in the following order:
if we have an array
[1, -1, -3, 9, -2, -5, 4, 8,]
we must rearrange it this way: largest number, smallest number, 2nd largest number, 2nd smallest number, ...
[9, -5, 8, -3, 4, -2, 1, -1 ]
I get the first largest and smallest numbers, but can't figure out how to make it dynamic for all values in the array.
I know that I must take two variables, say firstSmallest and firstLargest and point them to the first and last index of the array respectively, run a loop, which I do already in the code below, and store value into new array by incrementing firstSmallest and decrementing firstLargest, but couldn't implement into code.
let unsortedArr = [1, 5, 8 , 7, 6, -1, -5, 4, 9, 5]
let output = [];
function meanderArray(unsorted){
let sorted = unsorted.sort((a, b) => a-b);
let firstSmallest = sorted[0];
let firstLargest = sorted[unsorted.length-1];
for(let i = 0; i <= sorted.length; i++){
//I should increment firstSmallest and decrement firstLargest numbers and store in output
}
return output;
}
meanderArray(unsortedArr);
console.log(output);
You could take a toggle object which takes the property of either the first item or last from an array and iterate until no more items are available.
function meanderArray([...array]) {
const
result = [],
toggle = { shift: 'pop', pop: 'shift' };
let fn = 'shift';
array.sort((a, b) => a - b);
while (array.length) result.push(array[fn = toggle[fn]]());
return result;
}
console.log(...meanderArray([1, 5, 8, 7, 6, -1, -5, 4, 9, 5]));
You can sort an array by descending, then logic is the following: take first from start and first from end, then second from start-second from end, etc.
let unsortedArr = [1, 5, 8 , 7, 6, -1, -5, 4, 9, 5]
let output = [];
function meanderArray(unsorted){
let sorted = unsorted.sort((a, b) => b-a);
let output = []
for(let i = 0; i < sorted.length/2; i++){
output.push(sorted[i])
if(i !== sorted.length - 1 - i){
output.push(sorted[sorted.length - 1 - i])
}
}
return output;
}
let result = meanderArray(unsortedArr);
console.log(result);
You can sort, then loop and extract the last number with pop() and extract the first number with shift().
let unsortedArr = [1, -1, -3, 9, -2, -5, 4, 8,]
let output = [];
function meanderArray(unsorted){
let sorted = unsorted.sort((a, b) => a - b);
for(let i = 0; i < unsortedArr.length + 2; i++){
output.push(sorted.pop());
output.push(sorted.shift());
}
console.log(output);
return output;
}
meanderArray(unsortedArr);
Fastest Meandering Array method among all solutions mentioned above.
According to the JSBench.me, this solution is the fastest and for your reference i have attached a screenshot below.
I got a different approach, but i found that was very close to one of above answers from elvira.genkel.
In my solution for Meandering Array, First I sorted the given array and then i tried to find the middle of the array. After that i divided sorted array in to two arrays, which are indices from 0 to middle index and other one is from middle index to full length of sorted array.
We need to make sure that first half of array's length is greater than the second array. Other wise when applying for() loop as next step newly created array will contains some undefined values. For avoiding this issue i have incremented first array length by one.
So, always it should be firstArr.length > secondArr.length.
And planned to create new array with values in meandering order. As next step I created for() loop and try to push values from beginning of the first array and from end of the second array. Make sure that dynamically created index of second array will receive only zero or positive index. Other wise you can find undefined values inside newly created Meandering Array.
Hope this solution will be helpful for everyone, who loves to do high performance coding :)
Your comments and suggestions are welcome.
const unsorted = [1, 5, 8, 7, 6, -1, -5, 4, 9, 5];
const sorted = unsorted.sort((a,b)=>a-b).reverse();
const half = Math.round(Math.floor(sorted.length/2)) + 1;
const leftArr = sorted.slice(0, half);
const rightArr = sorted.slice(half, sorted.length);
const newArr = [];
for(let i=0; i<leftArr.length; i++) {
newArr.push(leftArr[i]);
if (rightArr.length-1-i >= 0) {
newArr.push(rightArr[rightArr.length-1-i]);
}
}
I'm using a JSON array that has 38 fields and I have to sum each of the fields.
I've tried a small sample set to test how and I'm running into a problem:
I have variables called field1, field2, .... So I figured out how to create them but it looks like it sees the variables as text not the values inside them.
test1 = [[36,1500,2,3,4],[36,15,2,7,8],[36,3000,4,5,6],[36,8,7,6,15]]
for (var i = 0; i < test1.length; i++) { //get each array
for (var n = 0; n < 5; n++) { //get each item in the array
var theField = "field" + n;
theField = theField +test1[i][n]; //This fails
field2 = field2 + test1[i][2]; //This works
If I use field + n to create the variable, the sum value at the end is 0,
if I call field2 = field2 + test1[i][2] at the end of the loop I have the sum for the third value in each array. However I have to hard code field1 -> field38.
Sum by Array
let data = [
[36, 1500, 2, 3, 4],
[36, 15, 2, 7, 8],
[99, 99, 99, 99 ,99], // fake data
[36, 3000, 4, 5, 6],
[36, 8, 7, 6, 15]
]
// only pull records with 36 as value of first element
let filtered = data.filter(arr=>arr[0]==36)
// iterate over each array and sum elements at index 1...end
let sum = filtered.map(([_,...arr]) => arr.reduce((sum, val) => sum += +val, 0))
console.log(sum)
Filter the data for what you want based on the first element (stored in filtered)
Iterate over the remaining array using map(), which will return an array
Inside the map() loop, iterate over each value using reduce() to sum each element of the array
The first element is ignored using destructuring assignment/rest. The [_,...arr] means the first element will be stored in a variable called _, the rest of the elements will be stored in an array called arr, which is elements 2..5 (index:1..4).
Sum by Index
let data = [
[36, 1500, 2, 3, 4],
[36, 15, 2, 7, 8],
[99, 99, 99, 99, 99], // fake data
[36, 3000, 4, 5, 6],
[36, 8, 7, 6, 15]
]
// only pull records with 36 as value of first element
let filtered = data.filter(arr => arr[0] == 36)
// initialize array to hold sums
let sums = Array(filtered[0].length).fill(0)
// iterate over each array and each index to sum value at the index
filtered.forEach(([_, ...arr]) =>
arr.forEach((val, ndx) =>
sums[ndx] += +val
)
)
console.log(sums)
Filter the data for what you want based on the first element (stored in filtered)
Initialize the holding array of sums by index to 0
Iterate over the filtered array (rows) using forEach
Inside the forEach() loop, iterate over each value using another forEach() using the index and value to store to the sums array created earlier
The first element is ignored using destructuring assignment/rest. The [_,...arr] means the first element will be stored in a variable called _, the rest of the elements will be stored in an array called arr, which is elements 2..n (index:1..n-1).
What are you trying to do? If you are trying to sum the arrays.
const test1 = [[36,1500,2,3,4],[36,15,2,7,8],[36,3000,4,5,6],[36,8,7,6,15]];
const sum = arr => arr.reduce((total, item) => total + item, 0);
console.log(test1.reduce((total, arr) => sum(arr) + total, 0));
You can simply use map to map the array to a new array of sums using reduce to create a sum of the child arrays. This will give an array of sums, if you would like a sum of those, you can then use reduce again on that result and get a final number.
// Our starting data
let test1 = [[36,1500,2,3,4],[36,15,2,7,8],[36,3000,4,5,6],[36,8,7,6,15]]
// a simple reusable sum function
const sum = (sum, val) => sum + val
// Sum each array
let result = test1.map(arr => arr.reduce(sum, 0))
// Display the results (stringified for readability)
console.log('Each array sumed:', JSON.stringify(result))
// Sum the results if needed
console.log('Sum of all arrays:', result.reduce(sum, 0))
I have an array containing a bunch of arrays, where every one of these arrays has the same length and just two possible entries (+1 and -1 or +1 and 0). Now I'm searching for an efficient way to sort these arrays, so if I want to check if the list contains a given array, there is no need to compare the given array with every one in the list.
The list may look like this:
list = [
[1, 1, -1, 1],
[-1, 1, -1, 1],
[1, -1, -1, 1]
];
To be more precise, this list is created in a while loop, where the condition is that the new array created in the loop does not appear in the list yet. Therefore, it would be nice to sort the array on the fly, e.g. insert it at an appropriate place in the list.
To avoid misunderstandings, the loop now looks like this
var list = [];
var array = someArray;
while (indexOfArray(list, array) === -1) {
list.push(array);
array = calculateNewArray(array);
}
where indexOfArray is a function that returns the index of the array in list if existing, -1 otherwise, and calculateArray takes the previous array and returns a new one. The problem is that the list may get very long (thousands or tens of thousands of different arrays, which may have a length of several hundred entries), so always compare the new array with every saved array in the list becomes extremely time consuming.
What is an efficient and sound approach to tackle this problem?
Edit: to provide an example, the list above could be sorted as
sortedList = sortList(list)
// sortedList should look like this
[
[1, 1, -1, 1],
[1, -1, -1, 1]
[-1, 1, -1, 1],
]
where first comes the array containing as many +1 in a row as possible, second the one with least fewer +1 and so and, while the last array contains as many -1 as possible in a row.
You could use Sorting with map and simply add all values of the inner array.
// the array to be sorted
var list = [[1, 1, -1, 1], [-1, 1, -1, 1], [1, -1, -1, 1]];
// temporary array holds objects with position and sort-value
var mapped = list.map(function (el, i) {
return { index: i, value: el.reduce(function (a, b) { return a + b; }, 0) };
});
// sorting the mapped array containing the reduced values
mapped.sort(function (a, b) {
return b.value - a.value;
});
// container for the resulting order
var result = mapped.map(function (el) {
return list[el.index];
});
console.log(result);
Edit for in situ sorting
Basically, you have two options
use a property for it. (the property sum remains until it became deleted)
var list = [[1, 1, -1, 1], [-1, 1, -1, 1], [1, -1, -1, 1]];
list.forEach(function (el) {
el.sum = el.reduce(function (a, b) { return a + b; }, 0);
});
list.sort(function (a, b) {
return b.sum - a.sum;
});
console.log(list);
get for every sort loop the sums (does not need a property, but the perfomance is bad)
function sum(a, b) { return a + b; }
var list = [[1, 1, -1, 1], [-1, 1, -1, 1], [1, -1, -1, 1]];
list.sort(function (a, b) {
return b.reduce(sum, 0) - a.reduce(sum, 0);
});
console.log(list);
You could just map through the list and sort all the inner arrays with sort().
list = [
[1, 1, -1, 1],
[-1, 1, -1, 1],
[1, -1, -1, 1]
];
list = list.map(function(x){return x.sort(function(a,b){return a-b})});
console.log(list);
It could be even shorter with Javascript ES6:
list = list.map(x=>x.sort((a,b)=>(a-b)));
Given two arrays arr1 and arr2 that both have the same items, but are sorted differently, how to make a list of least number of item-move operations required to make arr1 match arr2?
The function / algorithm to do this should accept my two arrays as the only arguments, and return an array like this:
[
[1,5],
[3,0],
[7,2]
]
The above array would be interpreted as "Move item at index 1 to index 5, then move item at index 3 to index 0, and finally move item at index 7 to index 2."
By an item-move operation I mean the following:
function arrayMove(array, from, to) {
return array.splice(to, 0, array.splice(from, 1)[0]);
}
When an item is moved from index a to index b, items after index a "slide down" so the item that had index a + 1 now has index a, and when the item is added back at index b, the items that had an index >= b will slide up, so that the item that had index b would now have index b + 1.
Feel free to provide your algorithm in JS or pseudocode, any help appreciated.
This strikes me as related to the edit distance problem. Perhaps you could exploit the Wagner-Fischer algorithm.
Something like this perhaps?
Javascript
// swap two elements in an array by their indexes a and b and
// return an array of the swapped coordinates.
function swap(arr, a, b) {
// assign the value at index a to temp
var temp = arr[a];
// assign the value at index b to index a
arr[a] = arr[b];
// assign the value of temp to the value at index b
arr[b] = temp;
// coordinates of move
return [a, b];
}
// return an array of moved coordinates
function minMoves(arr1, arr2) {
// take a shallow copy of arr2 so that the original is not modified
arr2 = arr2.slice();
// apply a function against an accumulator (moves) for each value of
// the array (arr1) (from left-to-right)
return arr1.reduce(function (moves, item, index) {
// if the values of each array at the index are not the same
if (item !== arr2[index]) {
// swap the current indexed element of arr2 with the value of
// the correct element as indexed in arr1. Add the moved
// coordinates to the beginning of the accumulator
moves.unshift(swap(arr2, index, arr2.lastIndexOf(item)));
}
// return the accumulater for the next iteration
return moves;
}, []);
}
var before = [1, 5, 6, 3, 2, 4, 7, 8, 9, 0],
test = before.slice(),
after = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
moves = minMoves(before, after);
console.log('moves: ' + JSON.stringify(moves));
moves.forEach(function(move) {
swap(test, move[0], move[1]);
});
console.log('Should be ordered nicely: ' + JSON.stringify(test));
Output
moves: [[3,5],[2,5],[1,4]]
Should be ordered nicely: [1,2,3,4,5,6,7,8,9,0]
On jsFiddle
This is what I would do, it is not based on any research of algorithms that have been proven optimal.
And here is the code using your arrayMove method instead of swap
Javascript
function arrayMove(array, from, to) {
return array.splice(to, 0, array.splice(from, 1)[0]);
}
// return an array of moved coordinates
function minMoves(arr1, arr2) {
// take a shallow copy of arr2 so that the original is not modified
arr2 = arr2.slice();
// apply a function against an accumulator (moves) for each value of
// the array (arr1) (from left-to-right)
return arr1.reduce(function (moves, item, index) {
var last;
// if the values of each array at the index are not the same
if (item !== arr2[index]) {
// swap the current indexed element of arr2 with the value of
// the correct element as indexed in arr1. Add the moved
// coordinates to the beginning of the accumulator
last = arr2.lastIndexOf(item);
arrayMove(arr2, last, index);
moves.unshift([index, last]);
}
// return the accumulater for the next iteration
return moves;
}, []);
}
var before = [1, 5, 6, 3, 2, 4, 7, 8, 9, 0],
test = before.slice(),
after = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
moves = minMoves(before, after);
console.log('moves: ' + JSON.stringify(moves));
moves.forEach(function(move) {
arrayMove(test, move[0], move[1]);
});
console.log('Should be ordered nicely: ' + JSON.stringify(test));
Output
moves: [[3,4],[2,5],[1,4]]
Should be ordered nicely: [1,2,3,4,5,6,7,8,9,0]
On jsFiddle
Finally a jsPerf to compare the two methods.