Rebuilding _zip in Javascript using map, arbitrary arguments - javascript

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!

Related

JavaScript filter - What to return from jQuery filter callback method?

What do I need to return from my jQuery filter callback method in order to properly define the function, regardless if the result of the filter applied to my variable is not used after invocation of the filter method?
var results = [];
var filterResult = 0;
$(elem).each(function(){
results.push($(this).val().trim().toLowerCase())
});
var string = event.target.value.trim().toLowerCase();
results.filter(function (r) {
if(r == string)
filterResult = filterResult + 1;
return r; //<-- what needs to be returned for this callback to be properly defined
});
Array.prototype.filter creates a new Array by applying a function to each element of an existing array, and only including that element if the function returns true.
The reduce() method applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.
For example:
var arr = [-3,-2,-1,0,1,2,3];
var positive = arr.filter(function (num) {
return num > 0;
});
// num now contains: [1,2,3]
From your code snippet, that doesn't really seem like what you're trying to do. Rather it seems like you're trying to derive a single value from the elements of an array. In that case, what you want is Array.prototype.reduce:
var count = results.reduce(function (total, currentItem) {
if (currentItem == string) {
return total + 1;
} else {
return total;
}
}, 0);
The second argument there (0) is the starting value of total.

Why do changes made to a cell propagate to other cells in this 2 dimensional array created using fill?

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

Javascript use original array when creating subarray

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

Best way to sort multidimentional array

I have a multidimentional array
array = [];
array[0] = [1,3,8,4,9,5];
array[1] = [5,9,4,2,9,3,0];
array[2] = [5,2,6,1,3,8,4,9,5,17,2,9,3,0];
array[3] = [-1,0,20,6];
I want to sort it to get this result
[
[-1,0,0,0,1,1],
[2,2,2,3,3,3,3],
[4,4,4,5,5,5,5,6,6,8,8,9,9,9],
[9,9,17,20]
]
I have defined this function that helped me get this result
Array.prototype.sort_multi = function()
{
var arr = [];
this.forEach(function(element_arr,index_arr,array_arr)
{
element_arr.forEach(function(element,index,array)
{
arr.push(element);
});
});
arr.sort(function (a, b) { return a - b;});
this.forEach(function(element_arr,index_arr,array_arr)
{
element_arr.forEach(function(element,index,array)
{
array_arr[index_arr][index] = arr.shift();
});
});
return this;
}
My question is: is there a simple way to do this ? for example using the function sort, or a simple algorithm ... ?
A slightly simplified (and slightly more efficient) version of sort_multi():
Basically what is happening here is:
The items in the original array are combined into one big array (for easy sorting)
We record the lengths of the original child arrays while joining
Sort the joined array numerically (as in the code you posted)
Split the sorted big array as per the lengths of the original child arrays
Return the result
Why this is more efficient than the original code:
The original code iterates through each child array element by element for joining. This is not needed since we have the natively implemented concat() for exactly that.
The original code again iterates element by element when splitting the joined/sorted array - Not needed since we have splice().
Array.prototype.sort_multi = function() {
var joined = [], lens = [], result = [];
this.forEach(function(item) {
joined = joined.concat(item);
lens.push(item.length); // store the initial lengths
});
joined = joined.sort(function (a, b) { return a - b;}); // sort numerically
lens.forEach(function(item) { // for each length in lens
result.push(joined.splice(0, item));
});
return result;
}

Get first element of a sparse JavaScript array

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?

Categories