I am making a recursive multidimensional array in javascript. But with a matrix I find it difficult.
For example, when I do this: matrix([2,3,4]) I want it to return this to me:
[ [ [ 0, 1, 2, 3 ]
, [ 0, 1, 2, 3 ]
, [ 0, 1, 2, 3 ]
]
, [ [ 0, 1, 2, 3 ]
, [ 0, 1, 2, 3 ]
, [ 0, 1, 2, 3 ]
] ]
The length of the entered matrix must be the number of dimensions and the numbers must be the value of the dimensions, having a 3D 2x3x4 matrix (height, width and height).
Code:
function copyArray(A)
{
var B=[]
for(var i=0;i<A.length;i++)
{
B[i]=A[i]
}
return B
}
function matrix(dims)
{
var I=dims[0]
dims.shift()
var A=[]
A.length=I
for(var i=0;i<I;i++)
{
var dims2=copyArray(dims)
A[i]=matriz(dims)
dims=dims2
}
return A
}
The code I have generates the following error:
Uncaught RangeError: Invalid array length(…)
You can do it this way, but it should be mentioned first:
Array(length): to create an array of the specified length.
.shift(): to remove the first element from the array.
dims.length ?: to see if the recursive function should still be
executed.
dims.slice(0): to clone the array passed to the function.
function matrix(dims) {
var arr = Array(dims.shift() || 0);
for(var idx = 0; idx < arr.length; idx++) {
arr[idx] = dims.length ? matrix(dims.slice(0)) : idx;
}
return arr;
}
console.log( matrix([2,3,4]) )
.as-console-wrapper {max-height: 100%!important;top:0}
Use a recursive function for this... build the level according to the dimension remove the index of the level you have dealt and move foward... do this until there no more dimensions to be handled.
This is an example of how to do it...
const createRange = (FROM, TO) => [...Array(TO - FROM + 1).keys()].map(i => i + FROM);
const createMatrix = (dimensions) => {
const dim = dimensions[0];
const newDimensions = dimensions.slice(1, dimensions.length);
if (!newDimensions.length) return createRange(0, dim - 1);
return [...Array(dim).keys()]
.map(_ => createMatrix(newDimensions));
};
console.log(
createMatrix([2,3,4])
)
Another approach using Array.from() and it's built in mapper
const matrix = (dims) => (
Array.from({length: dims.shift()}, (_,i) => dims.length ? matrix([...dims]) : i)
)
console.log(matrix ([2,3,4]))
Related
How do I create a subarray from an existing array in Javascript?
For example;
Arr = [5,2,1,2]
Then I want to insert 8 in position 1 of Arr, but keep the original value 2. So arr can become:
Arr = [5,[2,8],1,2]
I tried using concat, which sort of did something but duplicates all values.
Bear in mind that this can grow e.g. Arr = [5,[2,8,3,4],1,[2,3]]
Thanks!
You could assign the concatinated values.
const
addAt = (array, value, index) => array[index] = [].concat(array[index], value),
array = [5, 2, 1, 2];
addAt(array, 8, 1);
console.log(array)
addAt(array, 3, 1);
console.log(array)
.as-console-wrapper { max-height: 100% !important; top: 0; }
There are several different ways to do this, but you can reassign the current array index to an array like so:
Arr[1] = [Arr[1], 8]. Then if you wanted to continue adding to the array at index 1 in Arr, you could do something like Arr[1].push(x).
You could do something like this (Probably not the best answer, but may be helpful)
const addIntoArray = (arr, toAdd, index) => {
arr[index] = typeof arr[index] == "number" ? [arr[index], toAdd] : [...arr[index], toAdd];
return arr
}
Arr = [5,2,1,2]
console.log(Arr); // [ 5, 2, 1, 2 ]
addIntoArray(Arr, 1, 1)
console.log(Arr); // [ 5, [ 2, 1 ], 1, 2 ]
addIntoArray(Arr, 3, 1)
console.log(Arr) // [ 5, [ 2, 1, 3 ], 1, 2 ]
Basically ...arr expands the array and then we add toAdd at it's end and [arr[index], toAdd] creates an array with the first element as the number and the second the new element. (It modifies the original array, as shown in the example, so pay attention as it may lead to bugs)
The typeof arr[index] == "number"is just a simple/generic typecheck to see if there isn't an array already
This function should satisfy your conditions at basic level
// a - array, v - value to insert, i - position to insert
const avi = (a, v, i) => {
r = a.slice(0, i);
r.push([a[i], v]);
a.slice(i+1, a.length).forEach(e => r.push(e));
return r;
}
console.log(JSON.stringify(avi([5,2,1,2], 8, 1)))
//=> "[5,[2,8],1,2]"
I was doing some training tasks for my JS course and I got one where you must implement a function that takes a positive integer (n) and returns a matrix like the one below (5 was passed):
[ [ 1, 0, 0, 0, 0 ],
[ 0, 1, 0, 0, 0 ],
[ 0, 0, 1, 0, 0 ],
[ 0, 0, 0, 1, 0 ],
[ 0, 0, 0, 0, 1 ] ]
I was able to implement the function with the following code:
function getIdentityMatrix(n) {
const mat = new Array(n).fill([]);
return mat.map((row, index) => {
row = new Array(n).fill(0);
row[index] = 1;
return row;
});
}
But while doing it I found a strange behavior that I can't explain... If I alter the code a little:
function getIdentityMatrix(n) {
const mat = new Array(n).fill(new Array(n).fill(0));
return mat.map((row, index) => {
row[index] = 1;
return row;
});
}
It returns a matrix like this:
[ [ 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1 ] ]
Why would it work that way? It's like the forEach function iterates over all the elements nested inside each row which it shouldn't do.
Thank you for any advise!
It's because Array is a reference type. When you do
new Array(n).fill(new Array(n).fill(0))
first the inner new Array(n).fill(0) makes an array size n filled with 0; next the outer Array(n).fill creates an array filled with n references to that inner array.
It doesn't create n inner arrays, just n references to the same array. So when you change an element of that inner array, all the references in the outer array will reflect the change since they all point to the same object.
The code in question is equivalent to this:
let n = 5
let innerArr = new Array(n).fill(0)
function getIdentityMatrix(n) {
const mat = new Array(n).fill(innerArr);
return mat.map((row, index) => {
row[index] = 1;
return row;
});
}
console.log(getIdentityMatrix(n))
Because you are using fill you are basically filling that mat array with references to the innerArr (which you can see clearly from the above console output).
Then you do row[index] = 1 for each i which is changing the same values (at i index) of the same array.
Now your working example ... which could be written in a shorter form as:
const getIdentityMatrix = (n) =>
[...Array(n)].map((row, index) => {
row = Array(n).fill(0)
row[index] = 1
return row
})
console.log(getIdentityMatrix(3))
Clearly maps over a newly created and then spreaded array of n but then overwrites each element with an entirely new array reference.
Since that reference is brand new modifying it with row[index] = 1 produces the expected behavior when we return the x from the map.
Another way to achieve this in one line is via map, Object.assign and Object.values like this:
const gm = (n) => [...Array(n)].map((x,i) =>
Object.values(Object.assign(Object.assign({}, Array(n).fill(0)), {[i]:1})))
console.log(gm(3))
// your example is roughly equivalent to this.
const innerArray = new Array(n).fill(0);
const mat = new Array(n).fill(innerArray);
(mat[0] === mat[1] === innerArray) === true;
there is only 1 nested array, not n times array.
Given an array with a minimum length of 3 and a maximum length of 5, which always contains uniquely occurring integers from 0 to 4 in ascending order, I need to pick out two non-consecutive numbers from it. Non-consecutive refers to their numeric value, not their position in the array.
To clarify, here are examples of valid arrays:
[ 1, 2, 3 ]
[ 0, 1, 2, 4 ]
[ 0, 3, 4 ]
For the arrays above, valid answers could be, respectively:
[ 1, 3 ]
[ 0, 2 ], [ 0, 4 ] or [ 1, 4 ]
[ 0, 3 ] or [ 0, 4 ]
Furthermore, in those cases where there is more than one valid answer, I need it to be selected at random, if at all possible (for instance I don't want to favor sequences that begin with the lowest number, which is what would occur if I always began checking from left to right and stopped checking as soon as I found one valid solution).
What would be the most efficient way of tackling this problem in Javascript?
You could use two nested iterations and build an new array for choosing as random result.
function getNonConsecutives(array) {
return array.reduce((r, a, i, aa) => r.concat(aa.slice(i + 2).map(b => [a, b])), []);
}
console.log(getNonConsecutives([ 0, 1, 2, 4 ]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
According to Bee157's answer, you could use a random choice with a constraint, like length for the first index and add the needed space for the second index.
The problem is, due to the nature of choosing the first number first, the distribution of the result is not equal.
function getNonConsecutives(array) {
var i = Math.floor(Math.random() * (array.length - 2));
return [
array[i],
array[Math.floor(Math.random() * (array.length - 2 - i)) + 2 + i]
];
}
console.log(getNonConsecutives([ 0, 1, 2, 4 ]));
demoFn(array) {
var i,j, y =[];
for (i=0; i<=array.length;i++) {
for (j = i + 1; j <= array.length; j++) {
if (array[j] && array[i]) {
if (array[j] !== array[i] + 1) {
y.push([array[i], array[j]]);
}
}
}
}
}
Take a random array and check it.
You can create a function using recursion that will pick random number in each iteration and loop all other elements and if condition is met add to array.
function findN(data) {
data = data.slice();
var r = []
function repeat(data) {
if (data.length < 2) return r;
var n = parseInt(Math.random() * data.length);
data.forEach(function(e, i) {
if (i != n) {
var a = data[n];
if (Math.abs(a - e) != 1 && r.length < 2) r.push(n < i ? [a, e] : [e, a])
}
})
data.splice(n, 1);
repeat(data)
return r;
}
return repeat(data)
}
console.log(findN([1, 2, 3]))
console.log(findN([0, 1, 2, 4]))
console.log(findN([0, 3, 4]))
Something like this should do it:
const pick = nums => {
// Pick a random number
const val = nums[Math.floor(Math.random() * nums.length) + 0];
// Filter out any numbers that are numerically consecutive
const pool = nums.filter(n => Math.abs(n - val) > 1);
// Pick another random number from the remainer
const other = pool[Math.floor(Math.random() * pool.length) + 0];
// Sort + return them
return [val, other].sort();
};
console.log(pick([0, 1, 2, 4]));
since you state that the array ellemnts are all unique, and that they are sorted.
It should suffice to take an random element
var index1=Math.floor(Math.random()*arr.length)
now any other element (except maybe the elemnts on position (index1 +/- 1) are not consecutive
So a new random element can be chosen excluding the first index.
var index2=Math.floor(Math.random()*arr.length);
if(index2==index1){
index2+=((index2<arr.length-1)?1:-1);
}
if(Math.abs(arr[index1]-arr[index2])<=1){
if(index2==0 && arr.length<4){
//set index2 to arr.length-1 and do check again, if not ok=> no result
if(!(arr[index1]-arr[arr.length-1]>=-1)){
return [arr[arr.length-1],arr[index1]];
}
}
else if(index2==arr.length-1 && arr.length<4){
//set index2 to 0 and do check again, if not ok=> no result
if(!(arr[index1]-arr[0]<=1)){
return [arr[0],arr[index1]];
}
}
else{
//if index2>index1 index2++
//else index2--
//always OK so no further check needed
index2+=(index2>index1?1:-1);
return [arr[index1],arr[index2]];
}
}
else{
//ok
return [arr[index1,arr[index2]];
}
return false;
if speed is not important, you can use a filter on the array to calculate a new array with all elements differing more then 1 unit of arr[index1]. and randomly select a new number from this new array.
Other attempt
function getNonConsecutive(arr){
var index1,index2,arr2;
index1=Math.floor(Math.random()*arr.length);
arr2=[].concat(arr);
arr2.splice((index1!==0?index1-1:index1),(index!==0?3:2));
if(arr2.length){
index2=Math.floor(Math.random()*arr2.length);
return [arr[index1],arr2[index2]];
}
else{
//original array has length 3 or less
arr2=[].concat(arr);
arr2.splice(index1),1);
for (var j=0,len=arr.length;j<len;j++){
if(Math.abs(arr1[index1]-arr2[j])>1){
return [arr[index1],arr2[j]];
}
}
}
return false
}
I have a button that has a function called clickNext(). Whenever that button is clicked, it increments the index position (scope.selected) on an array called 'arr1'.
<button type="button" class="right-btn col-xs-6" role="menuitem" ng-click="clickNext()">Next</button>
.
function clickNext()
{
scope.selected = (scope.selected + 1) % length;
}
arr1 = [
{apple: 1 , tango},
{banana: 3, kappa},
{orange:5, alpha},
{apple: 8 , beta},
{grape: 10 , sigma}
]
Problem
I have an identical array to arr1 called 'arr2'. What I'm trying to do is have the clickNext() increment to the next index position based on the arr2 array instead of the arr1 array.
Right now, the clickNext function still increments in the order of the arr1 array. For example, if I were to click the button, it would start on orange:5 then move to apple 8, etc.
arr2 = [
{orange:5, alpha},
{apple: 8 , beta},
{banana: 3, kappa},
{grape: 10 , sigma},
{apple: 1 , tango}
]
What I have tried
My though process to accomplish this is to use the findIndex() function and match the arr2 item to the arr1 item. That doesn't work, but maybe I'm structuring it wrong?
clickNext(){
var oldIndex = filteredToXs(scope.selected);
scope.selected = oldIndex + 1;}
function filteredToXs( filteredIndex ) {
var firstArr = scope.arr1[ filteredIndex ];
var xsIndex = scope.arr2.findIndex( function(item) {
return item.trackingNumber === firstArr.trackingNumber;
} );
if( xsIndex >= 0 ) return xsIndex;
if( xsIndex === -1 ) return 0; // Default value
}
I hope I understood your question correctly. Please read my comments in the code sections as well.
I had to modify your source so I was able to create a fiddle for you.
HTML: I changed the click event and removed a css class that's not available
<button type="button" role="menuitem" onclick="clickNext();">Next</button>
Sampe Arrays:
They were containing invalid objects: I changed alpha, beta, tango, .. to a property. You can also define them as values.. this shouldn't matter:
var arr1 = [
{ apple: 1, tango: '' },
{ banana: 3, kappa: '' },
{ orange: 5, alpha: '' },
{ apple: 8, beta: '' },
{ grape: 10, sigma: '' }];
var arr2 = [
{ orange: 5, alpha: '' },
{ apple: 8, beta: '' },
{ banana: 3, kappa: '' },
{ grape: 10, sigma: '' },
{ apple: 1, tango: '' }];
Code:
var idx = 0; //save current index of array 2
function clickNext() {
idx++;
//I'm comparing the array objects using a string compare- this only works because you said 'I have an identical array'
//this may cause issues if you're objects are cross-referenced
var find = function(array, obj) { //lookup value from arr2 in arr1
for (var i=0, l=array.length;i<l;i++)
if (JSON.stringify(array[i]) == JSON.stringify(obj)) //adjust this line to your needs
return array[i];
}
var result = find(arr1, arr2[idx])
if (!result)
throw new Error('not found- your arrays are not identical or you run out of index');
console.log(result);
}
fiddle: https://jsfiddle.net/k50y8pp5/
I have a list whose members are nested lists of integers, for example :
[ [1,2], [], [1,2,3], [ [1,2],3], [1,2,4], [ [], [1,2] ], [34,5,6], [-1,66] ]
I want to sort this list, using (what every other language in the world) would consider standard ordering of nested lists. For example :
[] < [ [1] ] < [ [1,2] ] < [ [2] ] < [ [11] ]
l.sort() messes this up, because it turns the lists into strings
Is there an easy way, either in javascript (or a common library like lodash) to get a proper sort of nested lists?
Here's a system of two mutually recursive functions, the first one compares arrays to non-arrays and numbers to numbers, the second compares arrays elementwise.
function cmp(x, y) {
let ax = Array.isArray(x),
ay = Array.isArray(y);
return ax - ay || (ax ? cmpArr(x, y) : x - y);
}
function cmpArr(x, y) {
let xlen = x.length,
ylen = y.length,
min = xlen < ylen ? xlen : ylen,
c;
for (let i = 0; i < min; i++) {
if (c = cmp(x[i], y[i]))
return c;
}
return xlen - ylen;
}
//
a = [[1, 2], [], [1, 2, 3], [[1, 2], 3], [1, 2, 4], [[], [1, 2]], [34, 5, 6], [-1, 66]];
a.sort(cmp);
console.log(JSON.stringify(a))
You can use _.sortBy as a shortcut.
_.sortBy(arr, function (o) { return o[0] || Infinity } )
Or, if your interior arrays are not yet sorted:
_.sortBy(arr, function (o) { return someMutatedArr[0] || Infinity } )
EDIT:
I found a better way that sorts beyond the first item in the list, but the empty array is still at the end. You could handle this as an edge case separately, annoying, I know.
var arr = [ [11], [1,3], [2], [], [1], [1,2]]
var count = []
// Get an array that is as long as the longest array with [0, 1, 2, etc]
arr.forEach(function (e, i) {
if (e.length > (count.length) )
count.push(count.length -1)
})
_.sortBy(arr, count)
EDIT : Okay, inspired by #Damien's answer, this is dirty but will work perfectly. Wish I had managed something cleaner.
You might be able to use lodash's differenceWith to 'compress' the array for each values wich are equals. But jsfiddle doesn't have the latest version of lodash so I can't test it.
var l = [[1,2],[],[1,3],[34, 5, 6],[-1, 66]];
l = l.sort(function(a, b) {
var minLength = Math.min(a.length, b.length);
for (var i = 0; i < minLength; i++) { // Compare the "matching pairs"
if (a[i] < b[i]) {
return -1;
} else if(a[i] > b[i]){
return 1;
}
}
return a.length - b.length; // If all pairs were equals, use list length
});
Fiddle
You can use a custom sort function, see this doc.
Example:
var l = [[1,2], [], [34,5,6], [-1,66]];
l = l.sort(function (a, b) {
if(!a.length) {
return -1;
}
if(!b.length) {
return 1;
}
return a[0] - b[0];
});
You probably have to handle more edge cases, but you have the idea here.
var res = _.chain(items)
.groupBy(function (item) {
return _.chain(item).flattenDeep().max().value() || -1;
})
.map(function(val, key){
return {
key: parseFloat(key),
val: val
};
})
.orderBy('key')
.map(function(item){
return _.sortBy(item.val, 'length');
})
.flatten()
.value();
for
[[], [ [1] ], [ [1,2] ], [ [2] ], [ [11] ]]
result is
[ [], [ [ 1 ] ], [ [ 1, 2 ] ], [ [ 2 ] ], [ [ 11 ] ] ]