Preventing a loop within a loop. How to reduce complexity - javascript

You have 2 arrays.
arrA is empty.
arrB is full of stuff (what it's full of doesnt matter, but assume it's huge).
When a user does a thing, an item is removed from arrB and that item is placed in arrA.
When a user does a different thing, it pulls items from arrA and puts it in arrB.
Is it possible to do this without having a loop within a loop?
Or to put it in computer science terminology:
Is it possible to do this with linear ( ϴ(n) ) time/space complexity?
Right now I have something that is at least ϴ(n*k)
(where n is the length of arrB, and k is the number of items passed to applyItems):
var arrA = [], arrB = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
function addToArray(arrayToAddTo, item){
if(arrayToAddTo.indexOf(item) === -1){
arrayToAddTo.push(item);
}
}
function removeFromArray(arrayToRemoveFrom, item){
var i = arrayToRemoveFrom.length;
var temp = [];
while(i--){
if(arrayToRemoveFrom[i] !== item){
temp.push(arrayToRemoveFrom[i]);
}
}
return temp;
}
function applyItems(arrayOfItems){
var i = arrayOfItems.length;
while(i--){
var current = arrayOfItems[i]
addToArray(arrA, current);
arrB = removeFromArray(arrB, current);
}
}
applyItems([0, 5, 3]);
console.log(arrA);
console.log(arrB);
applyItems works, but is not efficient.
Is it possible to decrease the time/space complexity here?

Based on my comment:
You can use native tools that will be way faster than manually looping. removeFromArray can use indexOf to get the position of the one to remove and then uses splice to remove it. Also you can work with reference instead of recreating the array every time.
with some other optimizations...
var arrA = [], arrB = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
function addToArray(item){
if(arrA.indexOf(item) === -1){
arrA.push(item);
}
}
function removeFromArray(item){
var index = arrB.indexOf(item);
if (index > -1) {
arrB.splice(index, 1);
}
}
function applyItems(arrayOfItems){
arrayOfItems.map(function(item) {
addToArray(item);
removeFromArray(item);
});
}
applyItems([0, 5, 3]);
console.log(arrA);
console.log(arrB);

Right now I have something that is at least ϴ(n*k)
You can make it O(n+k) by using an efficient lookup structure for arrayOfItems that does not require looping, but allows you to determine in O(1) whether an item should be swapped into the other array. With that, a single pass over arrB is enough.
Or if you sort your array and use binary search for the lookup, you will have O(log k * (n+k)). However if your array is finite and very small anyway, that hardly matters.
Also you should omit that indexOf test in addToArray. It seems to be established that no items are in both arrA and arrB (and your algorithm maintains that invariant), so you will not have to check for duplicates before pushing an item to the array.

Yes. Instead of making your own remove function, you can use splice().
So, instead of removeFromArray(arrB, current) you can just do arrB.splice(i, 1);. That will remove 1 element from index i.
You don't need to loop over every element to check if it matches the one you want - just use indexOf(). So you can do something like
var i = arrA.indexOf(item)

Related

Print array of numbers in Javascript in this order (first-last, second-last second....)

Write a "PrintZigZag" program that provides an array of 10 integers
containing any values and prints the elements in the following order:
the first-the last, the second-the last second, etc ... (The program
must be written pretending not to know what the values entered in the
array are)
This was a interview question for a position of "Junior Web Developer" and i didn't know how to solve it...
let numbers = [1,2,3,4,5,6,7,8,9,10];
this is the result you're looking for?
[1, 10, 2, 9, 3, 8, 4, 7, 5, 6]
I really enjoy interview questions, thankyou for sharing. please let me help explain what im doing here
let numbers = [1,2,3,4,5,6,7,8,9,10];
var result = [];
for(i=1; i< numbers.length+1 /2; i++) {
result.push(i);
result.push(numbers.pop());
}
I am looping over half the array starting at 1. Then I am pushing the first index onto the result array, and then straight after I am popping off the last number of the array.
This will build up a new array consisting of the first number and then the last number, eventually reaching the end.
Here's Sweet Chilly Philly's solution using .map. An interviewer will probably want to see you use the for loop, but knowing modern loops could help as well.
let arr = [];
numbers.map(item => {
arr.push(item);
arr.push(numbers[numbers.length - 1])
numbers.pop()
})
And here's a solution with a do, while loop as Peter S mentioned. Be careful with these since they can end in an infinite loop if not escaped correctly.
let arr = [];
do {
arr.push(numbers[0])
arr.push(numbers[numbers.length - 1])
numbers.shift()
numbers.pop()
} while (numbers.length > 0)

Does condition expression of for loop changes after each iteration?

I'm trying to write a function decreasingOrder which takes a positive integer as input and return an array of its digits in decreasing order.
e.g., decreasingOrder(1234) Should give [4,3,2,1].
function decreasingOrder(n) {
let unarr = [...`${n}`].map(i => parseInt(i)); //Unordered Array of Digits
let oarr = []; //Ordered Array of Digits
for(let j=0; j<unarr.length; j++){
let max = Math.max.apply(Math, unarr);
oarr.push(max);
unarr.splice(unarr.indexOf(max), 1); //delete element from array
}
return oarr;
}
console.log(decreasingOrder(1234));
//Expected [4,3,2,1], Instead got [4,3]
I think, deleting element using splice method also reduces the number
of iteration.
I also tried delete operator but get [4, NaN, NaN, NaN] (because Math.max([undefined])).
When I tried with specific number instead of unarr.length in condition expression for for loop, it works fine!
So when I use splice method to delete elements it reduces the unarr.length and when I tried to keep unarr.length constant using delete operator it gives NaN, what should I do? Is there any other way to write to the same function? I'm beginner in JavaScript.
The issue in your code is unarr.splice(unarr.indexOf(max), 1) inside loop.
By taking your example of console.log(decreasingOrder(1234)). In the first cycle the highest number from the array is found and is removed from the array and pushed to new array.
At the end of the first cycle the outputs will be unarr = [1, 2, 3], oarr = [4] and j=1
Likewise after second loop unarr = [1, 2], oarr = [4, 3] and j=2. Now the loop condition j < unarr.length is not satisfied hence the loop breaks. So the output will be [4, 3].
Instead you can use the below utility for your requirement.
function decreasingOrder(n) {
let unarr = [...`${n}`].map(i => parseInt(i)) //Unordered Array of Digits
return unarr.sort((a,b) => b-a)
}
console.log(decreasingOrder(1234))
Hope this helps.

Finding unique values in multiple arrays

I'm trying to solve a freeCodeCamp exercise with this goal:
Write a function that takes two or more arrays and returns a new array
of unique values in the order of the original provided arrays.
In other words, all values present from all arrays should be included
in their original order, but with no duplicates in the final array.
The unique numbers should be sorted by their original order, but the
final array should not be sorted in numerical order.
So what I do is concatenate all the arguments into a single array called everything. I then search the array for duplicates, then search the arguments for these duplicates and .splice() them out.
So far everything works as expected, but the last number of the last argument does not get removed and I can't really figure out why.
Can anybody please point out what I'm doing wrong? Please keep in mind that I'm trying to learn, so obvious things probably won't be obvious to me and need to be pointed out. Thanks in advance.
function unite(arr1, arr2, arr3) {
var everything = [];
//concat all arrays except the first one
for(var x = 0; x < arguments.length; x++) {
for(var y = 0; y < arguments[x].length; y++) {
everything.push(arguments[x][y]);
}
}
//function that returns duplicates
function returnUnique(arr) {
return arr.reduce(function(dupes, val, i) {
if (arr.indexOf(val) !== i && dupes.indexOf(val) === -1) {
dupes.push(val);
}
return dupes;
}, []);
}
//return duplicates
var dupes = returnUnique(everything);
//remove duplicates from all arguments except the first one
for(var n = 1; n < arguments.length; n++) {
for(var m = 0; m < dupes.length; m++) {
if(arguments[n].hasOwnProperty(dupes[m])) {
arguments[n].splice(arguments[n].indexOf(dupes[m]), 1);
}
}
}
//return concatenation of the reduced arguments
return arr1.concat(arr2).concat(arr3);
}
//this returns [1, 3, 2, 5, 4, 2]
unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);
Looks like you overcomplicated it a bit ;)
function unite() {
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
}
res = unite([1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]);
document.write('<pre>'+JSON.stringify(res));
Explanations
We split the problem into two steps:
combine arguments into one big array
remove non-unique elements from this big array
This part handles the first step:
[].concat.apply([], arguments)
The built-in method someArray.concat(array1, array2 etc) appends given arrays to the target. For example,
[1,2,3].concat([4,5],[6],[7,8]) == [1,2,3,4,5,6,7,8]
If our function had fixed arguments, we could call concat directly:
function unite(array1, array2, array3) {
var combined = [].concat(array1, array2, array3);
// or
var combined = array1.concat(array2, array3);
but as we don't know how many args we're going to receive, we have to use apply.
someFunction.apply(thisObject, [arg1, arg2, etc])
is the same as
thisObject.someFunction(arg1, arg2, etc)
so the above line
var combined = [].concat(array1, array2, array3);
can be written as
var combined = concat.apply([], [array1, array2, array3]);
or simply
var combined = concat.apply([], arguments);
where arguments is a special array-like object that contains all function arguments (actual parameters).
Actually, last two lines are not going to work, because concat isn't a plain function, it's a method of Array objects and therefore a member of Array.prototype structure. We have to tell the JS engine where to find concat. We can use Array.prototype directly:
var combined = Array.prototype.concat.apply([], arguments);
or create a new, unrelated, array object and pull concat from there:
var combined = [].concat.apply([], arguments);
This prototype method is slightly more efficient (since we're not creating a dummy object), but also more verbose.
Anyways, the first step is now complete. To eliminate duplicates, we use the following method:
combined.filter(function(elem, index) {
return combined.indexOf(elem) === index;
})
For explanations and alternatives see this post.
Finally, we get rid of the temporary variable (combined) and chain "combine" and "dedupe" calls together:
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
using the 3rd argument ("this array") of filter because we don't have a variable anymore.
Simple, isn't it? ;) Let us know if you have questions.
Finally, a small exercise if you're interested:
Write combine and dedupe as separate functions. Create a function compose that takes two functions a and b and returns a new function that runs these functions in reverse order, so that compose(a,b)(argument) will be the same as b(a(argument)). Replace the above definition of unite with unite = compose(combine, dedupe) and make sure it works exactly the same.
You can also try this :
var Data = [[1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]]
var UniqueValues = []
for (var i = 0; i < Data.length; i++) {
 UniqueValues = [...new Set(UniqueValues.concat(Data[i]))]
}
console.log(UniqueValues)

Does one of these programs have an advantage over the other? JavaScript

Having learned some Javascript from CodeAcademy, I wanted to try some exercises to test my knowledge.
The exercise was this
Write a JavaScript function which will take an array of numbers stored and find the second lowest and second greatest numbers, respectively. - See more at: http://www.w3resource.com/javascript-exercises/javascript-functions-exercises.php#EDITOR
My function was this
function checker(array) {
narray = array.sort();
console.log(narray);
console.log(narray[1] + "," + narray[array.length - 2]);
}
checker([1, 2, 3, 4, 5]);
Their function was this
function Second_Greatest_Lowest(arr_num)
{
arr_num.sort(function(x,y)
{
return x-y;
});
var uniqa = [arr_num[0]];
var result = [];
for(var j=1; j<arr_num.length; j++)
{
if(arr_num[j-1] !== arr_num[j])
{
uniqa.push(arr_num[j]);
}
}
result.push(uniqa[1],uniqa[uniqa.length-2]);
return result.join(',');
}
alert(Second_Greatest_Lowest([1,2,3,4,5]));
Do you see any reason why the second option would be better?
As noted, their answer wants the 2nd unique number and it is rather inefficient as well.
There are two differences between your solution and theirs:
sorting function
uniqueness
The implicit sorting function is a natural sort. This means that the objects are converted to strings and compared as strings:
[1, 2, 3, 11].sort() // [1, 11, 2, 3]
[1, 2, 3, 11].sort(function(a,b){ return a-b }); // [1, 2, 3, 11]
The second difference, the uniqueness of the numbers. Your solution gives the number on the second position in the array, while theirs gives the number with the second lowest value:
[1,1,2] // Your solution: 1,1
[1,1,2] // Their solution: 2,1
While you can argue that it is not required, that is a matter of definition.
Either way, an improvement could be made in their solution as well to make it more efficient, though not as readable:
function getNumbers(arr) {
if (!arr.length) throw new Error('arr has no items');
arr = arr.slice().sort(function(a,b){ return a-b }); // copy and sort the array
for (var i=0; arr[i] === arr[0]; i++);
if (i === arr.length) { // all numbers are identical
return [arr[0], arr[0]];
}
var a = arr[i]; // second lowest number
i = arr.length-1;
var b = arr[i];
while (arr[i] === b) i--;
return [a, arr[i]];
}
// usage
getNumbers([2,3,1,1,1,6,4,5,6,1,11]) //[2, 6]
As you can see, once you found the number you're interested in, you no longer iterate through the sorted array. Compared to the solution that computes an array of unique numbers, this one is far more efficient, especially for large arrays.
No. Your function is just better. Really.
Reason is that on w3resource.com wants to show you how it should work - it's tutorial. You are just practical programmer.
Both solutions will work also when you put string inside, or negative numbers ..
The text of an exercise is misunderstood. They SHOULD have ask for a second "unique" number from the beginning and from the end. Then:
1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6
Real answers are: 2, 5. Answers from Your script are 1 and 6.
So Your answer is OF COURSE great and OK, but their question is a bit inaccurate.
But a fact that You have note their mistake, and make a better algorithm, sugest that You can skip to another lesson :).

How to randomize subset of array in Javascript?

What is the best way to randomize part of the array in Javascript
For example, if I have 100 items in the array, what is the fast and efficient way of randomizing set of every 10 times. Items between 0 and 9 is randomize within data items[0] to items[9]. Items between 10 to 19 are randomize within data items[10] to items[19] and so on..
You can adjust the array shuffle method that is described here: http://jsfromhell.com/array/shuffle
It is based on Fisher-Yates (Knuth) algorithm (http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
I would just use the built in slice, concat and sort methods. Something like this.
function shuffle(arr, start, end) {
return arr.slice(start, end).sort(function() { return .5 - Math.random(); }).concat(arr.slice(end));
};
That's the basic idea, you probably want to make an algorithm to slice every 10 elements, sort and rejoin the sets.
EDIT: Read comments below for why this solution may not be suitable for your needs.
I would split the 1 array into 10 subsets, then perform a shuffle algorithm of your choice on those subsets, and then recombine in the correct order.
The following shuffles the specified chunk of an array in place, as randomly as the environment's random number generator will allow:
function shuffleSubarray(arr, start, length) {
var i = length, temp, index;
while (i--) {
index = start + Math.floor(i * Math.random());
temp = arr[index];
arr[index] = arr[start + i];
arr[start + i] = temp;
}
return arr;
}
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9];
alert( shuffleSubarray(a, 2, 5) );

Categories