I am new to Javascript and I have encountered the following problem:
I have an array of objects that remains unchanged. From this array, I would like to create subarrays with different start and end indexes.
var origArr = [o1, o2, o3, o4, o5];
// origArr .length = 5
var subArr = origArr.slice(1, 4);
// subArr = [o2, o3, o4];
Normally I can use the slice method. But this means that the elements are copied into the new array. Since all I want to do is the alter the start and end index, is there any easy way to do this without creating a new copy each time? I would just use the reference to the original array.
Thank you for your help.
Chris
You can't create a "view" of a subset of an array with standard JavaScript arrays. You can do it with the new typed arrays, but the new typed arrays only offer storage for various numeric formats. Your variable names (o1 and so on) suggest object references instead.
For standard arrays, you can use a function instead:
function subMapper(array, index, length) {
return function(i, value) {
if (index + i >= length) {
// Beyond the end of the array, do what you think is appropriate
}
if (arguments.length === 1) {
// Getting a value
return array[index + i];
}
// Setting a value
array[index + i] = value;
};
}
var origArr = [o1, o2, o3, o4, o5];
var subArrMapper = subMapper(origArr, 1, 4);
var x = subMapper(0);
console.log(x === o2); // true
Related
I have a problem with this 2-dimensional array in JS. When I change a[1][0], a[0][0] changes with it. Is there something wrong in the way I am initializing it? If yes, how can I initialize it properly?
>var a = Array(100).fill(Array(100).fill(false));
>a[0][0]
>false
>a[1][0]
>false
>a[1][0] = true
>true
>a[1][0]
>true
>a[0][0]
>true
var a = Array(100).fill(Array(100).fill(false));
a contains an array, each element of which references to an array. you are filling the outer array with an array which contains all false values. The inner array is being made only once and reference to the same array is passed to each element of outer array that is why if you perform an operation on one element it reflects on other elements as well.
This is actually equivalent to
var a1 = Array(100).fill(false);
var a = Array(100).fill(a1);
here a gets 100 elements all having reference to same array a1. So if you change one element of a, all elements change since they are references to same array.
you will need to fill each element in outer array with a new array. you can do something like this:
var a = [];
for(var i=0; i<100; i++)
a.push(Array(100).fill(false));
Your entire array will be filled with references to the same (second dimension) array object.
To fill it with distinct objects, you'd have to do something like this:
const a = Array(100).fill(false).map(x => Array(100).fill(false));
a[0][0] = true;
console.log(a[0][0]);
console.log(a[0][1]);
console.log(a[1][1]);
Note that the values in the initial array need to be explicitly set to something (in my example false, but could also be undefined), because the array created by the constructor is sparse and the map() function will only operate on properties that actually exist.
To work around that, you could use Array.from():
const a = Array.from(Array(100), x => Array(100).fill(false));
a[0][0] = true;
console.log(a[0][0]);
console.log(a[0][1]);
console.log(a[1][1]);
Your problem is that you are using the second Array(100).fill(false) in every position of the first array. Thats why when you change one value in the "second" dimension it will change in every single position of the array. If you want to avoid this you hve to create a new Array for each position of the initial.
var x = Array(100).fill().map(x => Array(100).fill(false));
what you are doing here is adding the same array object at each index of bigger array.
So when you do
Array(x).fill(Array(y).fill(false));
What you are doing actually is :
Array(x).fill(Y); // i.e. X.fill(Y)
Now whenever you change Y, you will get the same value at each index of X.
You should use loop to fill in data when data itself is an object(remember Array is object).
These looks like a duplicate question but I've always just used something like this
var x = new Array(10);
for (var i = 0; i < 10; i++) {
x[i] = new Array(10).fill(false);
}
x[5][5] = true
Credit: How can I create a two dimensional array in JavaScript?
Since arrays are reference types, creating an N Dimensional array in JS is not so trivial. You need to create a clone tool first.
Array.prototype.clone = function(){
return this.map(e => Array.isArray(e) ? e.clone() : e);
};
function arrayND(...n){
return n.reduceRight((p,c) => c = (new Array(c)).fill().map(e => Array.isArray(p) ? p.clone() : p ));
}
var arr = arrayND(2,3,4,"x")
console.log(JSON.stringify(arr));
arrayND takes indefinite number of integer arguments each designating the size of a dimension and the last argument is the fill value.
The fill function as described in mdn, copies the same value to all array indices. You can use the below utility to create clones of the array. But since i am using the slice operator, this is restricted to cloning arrays.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
if (!Array.prototype.clonefill) {
Object.defineProperty(Array.prototype, 'clonefill', {
value: function(value) {
// Steps 1-2.
if (this == null) {
throw new TypeError('this is null or not defined');
}
var O = Object(this);
// Steps 3-5.
var len = O.length >>> 0;
// Steps 6-7.
var start = arguments[1];
var relativeStart = start >> 0;
// Step 8.
var k = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);
// Steps 9-10.
var end = arguments[2];
var relativeEnd = end === undefined ?
len : end >> 0;
// Step 11.
var final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);
// Step 12.
while (k < final) {
O[k] = value.slice(0);
k++;
}
// Step 13.
return O;
}
});
}
Array(100).clonefill(Array(100))
a[0][0] = true
a[1][0]
false
I have an array of objects, like so:
arr = [{"timeslot":"6am7am","AVG(Monday)":10,"AVG(Tuesday)":11,"AVG(Wednesday)":7}]
Each object will always contain the "timeslot" property, and can contain any combination of the day-of-the-week properties, Monday through Sunday. Each day of the week may only be represented once in a single object.
I want to alter each object: specifically, the key names of the day-of-the-week properties only (the "timeslot" property will be unchanged"), to get an array like so:
newArr = [{"timeslot":"6am7am","Monday":10,"Tuesday":11,"Wednesday":7}]
My slightly unreadable solution works:
// Iterate the array of objects
results.forEach(function(o) {
// Iterate the object's properties
Object.keys(o).forEach(function(k) {
if(k.includes("AVG")) {
var len = k.length;
var pos = len - 1;
var newKey = k.slice(4, pos); // Extract the day of the week from the key name
o[newKey] = o[k]; // Create the new property with the same value and the new key-name
delete o[k]; // Delete the original property
}
});
});
How can I improve this solution?
Instead of mutating the original array by adding and removing keys from each object, Array#map the array into a new array, and recreate the objects using Array#reduce:
var arr = [{"timeslot":"6am7am","AVG(Monday)":10,"AVG(Tuesday)":11,"AVG(Wednesday)":7}];
var result = arr.map(function(obj) {
return Object.keys(obj).reduce(function(r, key) {
var k = key.includes('AVG') ? key.slice(4, -1) : key;
r[k] = obj[key];
return r;
}, {});
});
console.log(result);
I'm trying to learn JavaScript well and am practicing rebuilding some underscore functions. I'm trying to rebuild zip using map where there is an arbitrary number of arguments. Here is the solution I came up with, with pluck. I know that the underscore implementation itself uses _pluck, which internally uses Map, so I wanted to see if it was possible to do this with map...
_.zip = function() {
var argumentsArray = Array.prototype.slice.call(arguments);
var longestArray = argumentsArray.sort(function(a, b) {
return b.length - a.length
})[0];
//create and return an array that is as long as the longestArray:
var zipped = Array(longestArray.length);
// you want to push each element from each array onto an array with the length of the longestArray.
for (var i = 0; i < longestArray.length; i++) {
zipped[i] = _.pluck(argumentsArray, i)
};
return zipped;
}
I'm stuck on what to return inside the map function below. I know I have to do some sort of a loop up to the length of the longest element, since the returned array should be that long. How would I do that inside map? Or, should I just do two for loops instead of trying to use map?
zip = function() {
//get the longest input argument:
var argumentsArray = Array.prototype.slice.call(arguments);
var longestArray = argumentsArray.sort(function(a, b) {
return b.length - a.length
})[0];
//trying to use map here:
return map(argumentsArray, function(val){
return ?
})
};
console.log(zip([1, 2, 4], [1]))
// returns [[1, 1],[2, undefined],[4, undefined]]
Below I have attached what should be a working copy of your original implementation without the use of pluck using only maps.
var zip = function() {
var argumentsArray = Array.prototype.slice.call(arguments);
var longestArray = argumentsArray.sort(function(a, b) {
return b.length - a.length
})[0];
return longestArray.map(function(value, index, array) {
return argumentsArray.map(function(val, i, arr) {
return val[index];
});
});
};
The outer map over longestArray acts solely as a looping mechanism so it would be better suited to use a for-loop instead.
The inner loop maps over the array of arguments passed in and, using the current index of the outer map, returns the ith element of each argument array. Since map already returns a new array, each iteration of the inner map will return an array containing the ith elements for each argument array.
Below is another implementation using a for loop and a map.
function zip() {
//turn args into an array
var argumentsArray = Array.prototype.slice.call(arguments);
var returnArr = [];
//get length of longest array
var length = argumentsArray.reduce(function (prev, curr) {
//starter val is 0, if curr array is longer replace with its length
return (prev >= curr.length) ? prev : curr.length;
}, 0);
//push an array of the ith element of each of the argument arrays
//into the return array
for (var i = 0; i < length; i++) {
returnArr.push(argumentsArray.map(function (val) {
return val[i];
}));
}
return returnArr;
}
Also note that instead of sorting to find the largest array, I reduce over the array lengths to find and return the longest length.
Since js sort is in-place it will potentially change your arguments array order. Your returned array of zipped elements will then be ordered by their original array lengths. Doing it this way they will instead be in the order that the argument arrays were passed in. But both are valid zip implementations depending on what you need.
Hope this helps!
Using the code below, taken from page 95 of Stoyan Stefanov`s Object Oriented JavaScript, if you call
var my = new Triangle(5, 10);
my.toString()
You get this result.
"shape, 2D shape, Triangle"
My question relates to the first function (function Shape) in this code.
1) I know what length property normally does, but why is it important in this function Shape in the code result[result.length]. If the code is returning the array of strings "shape, 2D shape, Triangle", where is it taking the length of the names and what is it doing with the length of the names?
2) Can you please explain (using plain language) what the program is saying with result[result.length]? i.e. having a result inside a result.
Thanks
function Shape(){}
// augment prototype
Shape.prototype.name = 'shape';
Shape.prototype.toString = function(){
var result = [];
if (this.constructor.uber) {
result[result.length] = this.constructor.uber.toString();
}
result[result.length] = this.name;
return result.join(', ');
};
function TwoDShape(){}
// take care of inheritance
var F = function(){};
F.prototype = Shape.prototype;
TwoDShape.prototype = new F();
TwoDShape.prototype.constructor = TwoDShape;
TwoDShape.uber = Shape.prototype;
// augment prototype
TwoDShape.prototype.name = '2D shape';
function Triangle(side, height) {
this.side = side;
this.height = height;
}
// take care of inheritance
var F = function(){};
F.prototype = TwoDShape.prototype;
Triangle.prototype = new F();
Triangle.prototype.constructor = Triangle;
Triangle.uber = TwoDShape.prototype;
// augment prototype
Triangle.prototype.name = 'Triangle';
Triangle.prototype.getArea = function(){return this.side * this.height / 2;}
result[result.length] = this.name;
This is essentially a way to add a new piece to the array at the next position available (offset forward).
Arrays in Javascript start with 0, so when the first array piece is added, it will in effect do this:
result = [];
// result is empty, so result.length == 0
result[0] = this.name;
Then, when the next toString() method is called, it will take the result array "length" (count) and create a new array piece at that index:
// result has one piece, so result.length == 1
result[1] = this.name;
Then, when the next toString() method is called, it will again take the result array "length" (count) and create a new array piece at that index:
// result has two pieces, so result.length == 2
result[2] = this.name;
So that you have an array with three pieces, using indices of 0, 1, 2, or the count of the result array pieces at the moment the array piece was added.
The code assumes that result is going to be an array with consecutive integer keys. Such an array with length items is going to have indexes from 0 to length - 1. So by setting result[result.length] = something, what happens is that you add an item to that array, and the index for the new item is one higher than the previously last index.
In effect, it adds one item to the array while keeping the index numbering continuous, without leaving any empty spaces between the item indexes.
An array's length property is always the highest index plus 1, so adding an item at array[array.length] adds an item at the end of the array.
It's equivalent to array.push(...) and sometimes preferred because some (very) old browsers don't have a push method and sometimes it's faster (and sometimes it's slower).
I have an array of objects in javascript. I use jquery.
How do i get the first element in the array? I cant use the array index - as I assign each elements index when I am adding the objects to the array. So the indexes arent 0, 1, 2 etc.
Just need to get the first element of the array?
If you don't use sequentially numbered elements, you'll have to loop through until you hit the first one:
var firstIndex = 0;
while (firstIndex < myarray.length && myarray[firstIndex] === undefined) {
firstIndex++;
}
if (firstIndex < myarray.length) {
var firstElement = myarray[firstIndex];
} else {
// no elements.
}
or some equivalently silly construction. This gets you the first item's index, which you might or might not care about it.
If this is something you need to do often, you should keep a lookaside reference to the current first valid index, so this becomes an O(1) operation instead of O(n) every time. If you're frequently needing to iterate through a truly sparse array, consider another data structure, like keeping an object alongside it that back-maps ordinal results to indexes, or something that fits your data.
The filter method works with sparse arrays.
var first = array.filter(x => true)[0];
Have you considered:
function getFirstIndex(array){
var result;
if(array instanceof Array){
for(var i in array){
result = i;
break;
}
} else {
return null;
}
return result;
}
?
And as a way to get the last element in the array:
function getLastIndex(array){
var result;
if(array instanceof Array){
result = array.push("");
array.pop;
}
} else {
return null;
}
return result;
}
Neither of these uses jquery.
Object.keys(array)[0] returns the index (in String form) of the first element in the sparse array.
var array = [];
array[2] = true;
array[5] = undefined;
var keys = Object.keys(array); // => ["2", "5"]
var first = Number(keys[0]); // => 2
var last = Number(keys[keys.length - 1]); // => 5
I was also facing a similar problem and was surprised that no one has considered the following:
var testArray = [];
testArray [1245]= 31;
testArray[2045] = 45;
for(index in testArray){
console.log(index+','+testArray[index])
}
The above will produce
1245,31
2045,45
If needed you could exist after the first iteration if all that was required but generally we need to know where in the array to begin.
This is a proposal with ES5 method with Array#some.
The code gets the first nonsparse element and the index. The iteration stops immediately with returning true in the callback:
var a = [, , 22, 33],
value,
index;
a.some(function (v, i) {
value = v;
index = i;
return true;
});
console.log(index, value);
If you find yourself needing to do manipulation of arrays a lot, you might be interested in the Underscore library. It provides utility methods for manipulating arrays, for example compact:
var yourArray = [];
yourArray[10] = "foo";
var firstValue = _.compact(yourArray)[0];
However, it does sound like you are doing something strange when you are constructing your array. Perhaps Array.push would help you out?