Let's say I have a simple array like this:
var myArr = [0,1,2,3,4,5,6,7,8,9]
I'd like to extract a number of elements, starting from a specific index, like this:
myArr.getElementsFromIndex(index, numberOfElements)
where, unlike .slice(), if we hit the last index, elements from the start of the array should be returned instead (so that the total number of elements returned will always be respected). Either pure javascript or a library like underscore/lodash can be used.
Examples:
myArr.getElementsFromIndex(3, 5)
should return[3,4,5,6,7]
and
myArr.getElementsFromIndex(8, 5)
should return [8,9,0,1,2]
Use the below code
var myArr = [0,1,2,3,4,5,6,7,8,9];
function getElementsFromIndex(startIndex, num) {
var elems = [];
for(var iter = 0; iter<num; iter++) {
if(startIndex >= myArr.length) {
while(startIndex >= myArr.length) {
startIndex -= myArr.length;
}
}
elems.push(myArr[startIndex]);
startIndex++;
}
return(elems);
}
Array#slice takes a start and end index (not a start index and a number of elements).
Array#splice does what you want, except for the wrapping around (but also modifies the original array).
You can write a wrapper function using slice (which will not modify the original array):
function getElementsFromIndex(arr, start, numElements) {
if(start + numElements > arr.length) {
var endOfArr = arr.slice(start, arr.length);
var elementsFound = arr.length - start;
var restElements = getElementsFromIndex(arr, 0, numElements - elementsFound);
return endOfArr.concat(restElements);
}
return arr.slice(start, start + numElements);
}
This function returns what you require (see example), and even wraps around multiple times, if needed.
If you want to tie the function to arrays, in order to use it as you propose (ie. myArr.getElementsFromIndex(start, numElements)), you can add it to Array's prototype. You might want to look up arguments for/against modifying prototypes of built-in types, though.
Array.prototype.getElementsFromIndex = function(start, numElements) {
if(start + numElements > this.length) {
var endOfArr = this.slice(start, this.length);
var elementsFound = this.length - start;
return endOfArr.concat(this.getElementsFromIndex(0, numElements - elementsFound));
}
return this.slice(start, start + numElements);
};
See example of the last one here.
Add this to your js code:
Array.prototype.getElementsFromIndex = function (start, len) {
var newArray = [],
origArray = this,
i = start;
while (newArray.length < len) {
newArray.push(origArray[i++]);
if (i >= origArray.length)
i = 0;
}
return newArray;
}
You can use it exactly the way you wanted:
var myArr = [0,1,2,3,4,5,6,7,8,9];
alert(myArr.getElementsFromIndex(8, 5));
JSFIDDLE DEMO: http://jsfiddle.net/x6oy0krL/1
Maybe some people will say that it is not right to extend objects like Array, documentElement and so on, but the result here is as the OP wanted.
I want to say that the original array will not be modified, too.
Just concatenate the array to itself, then use slice:
function sliceWrap(arr, start, num) {
return arr.concat(arr).slice(start, start+num);
}
Another approach, which wraps around:
function sliceWrap2(arr, start, num) {
var result = [], i, end = start+num, len = arr.length;
for (i=start; i<end; i++) {
result.push(arr[i % len]);
}
return result;
}
Related
Iterator should print every value within range but its only printing alternate nos.
function iterator(rangeStart, rangeEnd) {
if (rangeStart == 0 && rangeEnd == 0) {
return null;
}
var iterate = function*(start = 0, end = 5, step = 1) {
let iterationcount = 0;
for (let i = start; i <= end; i += step) {
yield i;
iterationCount = i;
}
return iterationCount;
}
var values = iterate(rangeStart, rangeEnd);
var tmp = [];
while (values.next().value != undefined) {
tmp.push(values.next().value);
}
return tmp.join(",");
}
console.log(iterator(0, 10))
expected
[0,1,2,3,4,5,6,7,8,9,10]
Result
[1,3,5,7,9,10]
Every call to next will consume a value from the iterator, so the while condition is consuming a value that will therefore not get into tmp.
But... JavaScript allows you to consume values in much easier ways. For instance with Array.from or spread syntax you can collect all values from the iterator into an array.
Not your question, but:
iterationCount serves no purpose in your code, so just drop that part.
Why would the function behave differently when both range start and end are 0 than when start and end are both 10? I would remove that case. When the range end would be less than the start, it would make sense to exit, but that will happen anyway without any if statement.
The name iterator for your function is somewhat misleading, as the return value is not an iterator, but a comma separated string. I would therefore call it rangeToCsv
function rangeToCsv(rangeStart, rangeEnd) {
var iterate = function*(start = 0, end = 5, step = 1) {
for (let i = start; i <= end; i += step) {
yield i;
}
}
var values = iterate(rangeStart, rangeEnd);
return Array.from(values).join(",");
}
console.log(rangeToCsv(0, 10))
Just was performing simple task in JS which was to take integer as an input, divide it into single digits and multiply them ignoring all zeros in it.
I have solved it but had some troubles which were simply solved by changing the loop. I am just curious why the code did not work properly with the for loop and started to work as I it for for of loop. I could not find out the answer by my self. If somebody could tell where I am wrong.
First one works as intended, second one always returns 1.
function digitsMultip1(data) {
var stringg = data.toString().split("", data.lenght);
for (let elements of stringg) {
if (elements != 0) {
sum = parseInt(elements) * sum
} else {
continue
};
}
return sum;
}
console.log(digitsMultip1(12035))
function digitsMultip2(data) {
var sum = 1;
var stringg = data.toString().split("", data.lenght);
for (var i = 0; i > stringg.lenght; i++) {
if (stringg[i] != 0) {
sum = parseInt(stringg[i]) * sum
} else {
continue
};
}
return sum;
}
console.log(digitsMultip2(12035))
There is no big difference. for..of works in newer browsers
The for...of statement creates a loop iterating over iterable objects, including: built-in String, Array, Array-like objects (e.g., arguments or NodeList), TypedArray, Map, Set, and user-defined iterables. It invokes a custom iteration hook with statements to be executed for the value of each distinct property of the object.
Several typos
length spelled wrong
> (greater than) should be < (less than) in your for loop
Now they both work
function digitsMultip1(data) {
var sum=1, stringg = data.toString().split("");
for (let elements of stringg) {
if (elements != 0) {
sum *= parseInt(elements)
} else {
continue
};
}
return sum;
}
console.log(digitsMultip1(12035))
function digitsMultip2(data) {
var sum = 1, stringg = data.toString().split("");
for (var i = 0; i < stringg.length; i++) {
if (stringg[i] != 0) {
sum *= parseInt(stringg[i])
} else {
continue
};
}
return sum;
}
console.log(digitsMultip2(12035))
You might want to look at reduce
const reducer = (accumulator, currentValue) => {
currentValue = +currentValue || 1; return accumulator *= currentValue
}
console.log(String(12035).split("").reduce(reducer,1));
I have prepared 2 Javascript functions to find matching integer pairs that add up to a sum and returns a boolean.
The first function uses a binary search like that:
function find2PairsBySumLog(arr, sum) {
for (var i = 0; i < arr.length; i++) {
for (var x = i + 1; x < arr.length; x++) {
if (arr[i] + arr[x] == sum) {
return true;
}
}
}
return false;
}
For the second function I implemented my own singly Linked List, in where I add the complementary integer to the sum and search for the value in the Linked List. If value is found in the Linked List we know there is a match.
function find2PairsBySumLin(arr, sum) {
var complementList = new LinkedList();
for (var i = 0; i < arr.length; i++) {
if (complementList.find(arr[i])) {
return true;
} else {
complementList.add(sum - arr[i]);
}
}
return false;
}
When I run both functions I clearly see that the Linked List search executes ~75% faster
var arr = [9,2,4,1,3,2,2,8,1,1,6,1,2,8,7,8,2,9];
console.time('For loop search');
console.log(find2PairsBySumLog(arr, 18));
console.timeEnd(‘For loop search’);
console.time('Linked List search');
console.log(find2PairsBySumLin(arr, 18));
console.timeEnd('Linked List search');
true
For loop search: 4.590ms
true
Linked List search: 0.709ms
Here my question: Is the Linked List approach a real linear search? After all I loop through all the nodes, while my outer loop iterates through the initial array.
Here is my LinkedList search function:
LinkedList.prototype.find = function(data) {
var headNode = this.head;
if(headNode === null) {
return false;
}
while(headNode !== null) {
if(headNode.data === data) {
return true;
} else {
headNode = headNode.next;
}
}
return false;
}
UPDATE:
It was a good idea to go back and have another think of the problem based the comments so far.
Thanks to #nem035 comment on small datasets, I ran another test but this time with 100,000 integers between 1 and 8. I assigned 9 to the first and last position and searched for 18 to make sure the entire array will be searched.
I also included the relatively new ES6 Set function for comparison thanks to #Oriol.
Btw #Oriol and #Deepak you are right. The first function is not a binary search but rather a O(n*n) search, which has no logarithmic complexity.
It turns out my Linked List implementation was the slowest of all searches. I ran 10 iterations for each function individually. Here the result:
For loop search: 24.36 ms (avg)
Linked List search: 64328.98 ms (avg)
Set search: 35.63 ms (avg)
Here the same test for a dataset of 10,000,000 integers:
For loop search: 30.78 ms (avg)
Set search: 1557.98 ms (avg)
Summary:
So it seems the Linked List is really fast for smaller dataset up to ~1,000, while ES6 Set is great for larger datasets.
Nevertheless the For loop is the clear winner in all tests.
All 3 methods will scale linearly with the amount of data.
Please note: ES6 Set is not backward compatible with old browsers in case this operation has to be done client side.
Don't use this. Use a set.
function find2PairsBySum(arr, sum) {
var set = new Set();
for(var num of arr) {
if (set.has(num)) return true;
set.add(sum - num);
}
return false;
}
That's all. Both add and has are guaranteed to be sublinear (probably constant) in average.
You can optimize this substantially, by pre-sorting the array and then using a real binary search.
// Find an element in a sorted array.
function includesBinary(arr, elt) {
if (!arr.length) return false;
const middle = Math.floor(arr.length / 2);
switch (Math.sign(elt - arr[middle])) {
case -1: return includesBinary(arr.slice(0, middle - 1), elt);
case 0: return true;
case +1: return includesBinary(arr.slice(middle + 1), elt);
}
}
// Given an array, pre-sort and return a function to detect pairs adding up to a sum.
function makeFinder(arr) {
arr = arr.slice().sort((a, b) => a - b);
return function(sum) {
for (let i = 0; i < arr.length; i++) {
const remaining = sum - arr[i];
if (remaining < 0) return false;
if (includesBinary(arr, remaining)) return true;
}
return false;
};
}
// Test data: 100 random elements between 0 and 99.
const arr = Array.from(Array(100), _ => Math.floor(Math.random() * 100));
const finder = makeFinder(arr);
console.time('test');
for (let i = 0; i < 1000; i++) finder(100);
console.timeEnd('test');
According to this rough benchmark, one lookup into an array of 100 elements costs a few microseconds.
Rewriting includesBinary to avoid recursion would probably provide a further performance win.
first of all find2PairsBySumLog function is not a binary search, it's a kind of brute force method which parses all the elements of array and it's worst case time complexity should be O(n*n), and the second function is a linear search that' why you are getting the second method to run fastly, for the first function i.e. find2PairsBySumLog what you can do is initialize binary HashMap and check for every pair of integers in array kind of like you are doing in the second function probably like
bool isPairsPresent(int arr[], int arr_size, int sum)
{
int i, temp;
bool binMap[MAX] = {0};
for (i = 0; i < arr_size; i++)
{
temp = sum - arr[i];
if (temp >= 0 && binMap[temp] == 1)
return true;
binMap[arr[i]] = 1;
}
}
I'm trying to get whatever number is the most frequently occuring number in an array, so for an array containing 1,2,10,5,1 the result should be 1. The code I wrote returns me the frequency for each number, so 1 occurs twice, 2 occurs once, 10 occurs once etc. Any suggestions how I can fix my result?
function mode(arr) {
var uniqNum = {};
var numCounter = function(num, counter) {
if(!uniqNum.hasOwnProperty(num)) {
uniqNum[num] = 1;
} else {
uniqNum[num] ++;
}
};
arr.forEach(numCounter);
return uniqNum;
}
I've kept your code unchanged and added some extra statements. Here is the demo: http://codepen.io/PiotrBerebecki/pen/rrdxRo
function mode(arr) {
var uniqNum = {};
var numCounter = function(num, counter) {
if(!uniqNum.hasOwnProperty(num)) {
uniqNum[num] = 1;
} else {
uniqNum[num] ++;
}
};
arr.forEach(numCounter);
return Object.keys(uniqNum)
.sort((a,b) => uniqNum[b] - uniqNum[a]) // sort by frequency
.filter((val,ind,array) => uniqNum[array[0]] == uniqNum[val]) // leave only most frequent
.map(val => Number(val)); // convert text to number
}
console.log( JSON.stringify(mode([3,3,2,4,4])) ) // [3,4]
console.log( JSON.stringify(mode([2,4,3,3])) ) // [3]
I think it could be done only with a little modification to your forEach loop and the assistance of another auxiliary data structure:
function mode(arr) {
var freq = [], uniqNum = {}, i;
arr.forEach(function (num) {
uniqNum[num] = i = (uniqNum[num] || 0) + 1;
freq[i] = (freq[i] || []).concat(num);
});
return freq[freq.length - 1];
}
console.log(mode([1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 6, 7, 1, 6]));
With only one iteration over all the elements of the array we can gather enough information to print out the result:
uniqNum is the set you created to gather info about the element's frequency.
freq will be an array which last element will contain an array with the elements of higher frequency.
Fiddle. Hope it helps.
First we want to make an array where we count the number of occurrences of a certain value up to that point.
Then we use the reduce function to return an array of values read from the original array for the indexes whose values have the current max appearances. We redefine max and empty the final output array of modes (if new max is established) as we go along. We want this to be a collection in case there is a tie for maximum appearances.
Additional advantage of the below is that it doesn't require sort which is more expensive o(nlog n) and keeps the time complexity down to just linear. I also wanted to keep the functions used down to only two (map and reduce) as it is all that is need in this case.
edit: fixed a major bug uniqNum[e] += 1 instead of uniqNum[e] + 1 which went unnoticed as my initial case array was still returning expected result. Also made the syntax more concise in favor of more comments.
var arr = [1,2,10,5,1,5,2,2,5,3,3];
//global max to keep track of which value has most appearances.
var max = -1;
var uniqNum = {};
var modeArray = arr.map(function(e) {
//create array that counts appearances of the value up to that point starting from beginning of the input arr array.
if(!uniqNum.hasOwnProperty(e)) {
uniqNum[e] = 1;
return 1;
} else {
return uniqNum[e] += 1;
}
//reduce the above appearance count array into an array that only contains values of the modes
}).reduce(function (modes, e1, i) {
//if max gets beaten then redefine the mode array to only include the new max appearance value.
if(e1 > max){
//redefining max
max = e1;
//returning only the new max element
return [arr[i]];
//if its a tie we still want to include the current value but we don't want to empty the array.
}else if(e1 == max){
//append onto the modes array the co-max value
return[...modes, arr[i]];
}
return modes;
},[]);
alert(modeArray);
Here is a test you can run of my solution against #acontell. In my browser (Chrome with V8) my solution was around three-four times faster for arrays with large number of repeating values and even bigger advantage with distributions with lower number of repeating values. #acontell 's is a cleaner looking solution for sure, but definitely not faster in execution.
var arr = [];
for(var i=0; i < 100000; i++){
arr.push(Math.floor(Math.random() * (100 - 1)) + 1);
}
console.time("test");
test();
function test(){
var max = -1;
var uniqNum = {};
var modeArray = arr.map(function(e) {
//create array that counts appearances of the value up to that point starting from beginning of the input arr array.
if(!uniqNum.hasOwnProperty(e)) {
uniqNum[e] = 1;
return 1;
} else {
return uniqNum[e] += 1;
}
//reduce the above appearance count array into an array that only contains values of the modes
}).reduce(function (modes, e1, i) {
//if max gets beaten then redefine the mode array to only include the new max appearance value.
if(e1 > max){
//redefining max
max = e1;
//returning only the new max element
return [arr[i]];
//if its a tie we still want to include the current value but we don't want to empty the array.
}else if(e1 == max){
//append onto the modes array the co-max value
modes.push(arr[i])
return modes;
}
return modes;
},[]);
}
console.timeEnd("test");
console.time("test1");
test1();
function test1 () {
var freq = [],
uniqNum = {},
i;
arr.forEach(function(num) {
uniqNum[num] = i = (uniqNum[num] || 0) + 1;
freq[i] = (freq[i] || []).concat(num);
});
return freq[freq.length - 1];
}
console.timeEnd("test1");
I've tried as an exercise to solve this with native js functions.
var arr = [1,2,10,5,1];
// groupBy number
var x = arr.reduce(
function(ac, cur){
ac[cur]?(ac[cur] = ac[cur] + 1):ac[cur] = 1;
return ac;
}, {}
);
// sort in order of frequencies
var res = Object.keys(x).sort(
function(a,b){ return x[a] < x[b]}
);
res[0] has the most frequent element
I have an array of shots. I have been able to take that array and loop through it to get all shots that occurred on hole #1 and then rearrange them in order based on "shot_number". I now need to do this for every hole and to create an array for each hole (ex: holeArray1, holeArray2). I have attempted a number of solutions to increment x but if I do I end up missing some shots that occurred on certain holes.
How can I refactor this function to create this array for every hole without just copying and pasting the code and changing the variable x myself? Thank you for your help. I know I should be able to figure this one out but am struggling.
$scope.createHoleShotsArrays = function () {
var i = 0;
var x = 1;
var holeArray = [];
var len = $scope.shots.length;
for (; i < len; i++) {
if ($scope.shots[i].attributes.hole == x) {
holeArray.push($scope.shots[i]);
holeArray.sort(function (a, b) {
if (a.attributes.shot_number > b.attributes.shot_number) {
return 1;
}
if (a.attributes.shot_number < b.attributes.shot_number) {
return -1;
}
// a must be equal to b
return 0;
});
}
}
console.log(holeArray);
};
Push the items you want into arrays, and sort them once. I don't have cases to test the code. You may modified it a little if something goes wrong.
$scope.createHoleShotsArrays = function() {
var holeArrays = [];
$scope.shots.forEach(function(shot) {
if (holeArrays.length < shot.attributes.hole) {
holeArrays[shot.attributes.hole - 1] = [];
}
holeArrays[shot.attributes.hole - 1].push(shot);
});
holeArrays.forEach(function(arr) {
arr.sort(function(a, b) {
return a.attributes.shot_number - b.attributes.shot_number;
});
});
console.log(holeArrays);
};