Time complexity (Big-O) of converting an array to a Set - javascript

So there is multiple ways to convert an Array to a Set in JS.
Example #2 is definitely O(n) since is iterating through all the elements of the array. is that the same case for Example #1? or JS does do some optimization in the background for us?
If yes, are there any downsides to using Example #1?
Example 1
const arr = [ 1, 3, 2, 3, 5 ];
const set = new Set(arr);
console.log(set);
/*
Output: Set { 1, 3, 2, 5 }
*/
Example 2
const arr = [ 1, 3, 2, 3, 5 ];
const set = new Set();
arr.map(item => set.add(item));
console.log(set);
/*
Output: Set { 1, 3, 2, 5 }
*/

It's still O(n); there's no magical way for JS to put all n elements in the Set without actually iterating through all n elements. The only way to get below O(n) would be to skip some of the elements, which is clearly impossible if all of them must be considered for inclusion in the Set.

Related

sorting an array according to the order of another array - pair inputs [duplicate]

This question already has answers here:
sort 2 array with the values of one of them in javascript
(4 answers)
Closed 2 years ago.
so I have two arrays - arrOne = [10, 2, 3, 14, 1] and arrTwo = [1, 2, 3, 5, 4];
I want sort the arrTwo and use the same indexing changes on arrOne, ie arrTwo = [1, 2, 3, 4, 5], arrOne [10, 2, 3, 1, 14].
I've been trying to implement it with merge sort, but it does not work. The recursion obstructs me from doing what I intended.
Important to note, I am getting the data as two integers at a time and push them into separate arrays, using the previous arrays that would mean -
input 10 ,1
input 2, 2
input 3, 3
input 14, 5
input 1, 4
Perhaps a different data structure could be used, but I am not aware of it.
I have put go as a tag since I would like to solve it both languages.
Create a 2d array which holds value of 2 arrays and extract 2 arrays after sorting.
let arrOne = [10, 2, 3, 14, 1],
arrTwo = [1, 2, 3, 5, 4];
arrOne
// create 2d array which contains both array values
.map((v, i) => [v, arrTwo[i]])
// sort the combined array based on first array element
.sort(([a], [b]) => a - b)
// update the main arrays
.forEach(([v1, v2], i) => {
arrOne[i] = v1;
arrTwo[i] = v2;
})
console.log(arrOne, arrTwo)
To solve this I would not use two arrays.
I would push an object to a single array. Making it much more structured keeping the data “together”
const data = [{
one: 10,
two: 1
},
{
one: 2,
two: 2
},
{
one: 3,
two: 3
},
{
one: 14,
two: 5
},
{
one: 1,
two: 4
}
To add another input:
data.push({
one: 200,
two: 6
})
Then sort by key “two”
data.sort((a, b) => {
return a.two-b.two
})
Just note that the sort will mutate the array but you can copy if this is an issue. Guessing sorting the original array is not a problem for your use case anyway.

Does 'array[array.length - 1] = array.pop()' yield undefined behavior?

I've been trying to implement a method which takes an array and an index as input, copies the last element of the given array into the entry at the given index, and then truncates the last element (i.e., sets the array shorter by one).
Here is the function which I was given as a suggestion for this task:
function swapWithLastAndRemove(array, index) {
array[index] = array.pop();
}
I then tested this function to make sure that it works on any given index:
for (let index = 0; index < 5; index++) {
var array = [0, 1, 2, 3, 4];
swapWithLastAndRemove(array, index);
console.log(array);
}
And I found it that it works correctly for all but the last index:
[ 4, 1, 2, 3 ]
[ 0, 4, 2, 3 ]
[ 0, 1, 4, 3 ]
[ 0, 1, 2, 4 ]
[ 0, 1, 2, 3, 4 ]
In other words, for the last element, it pops it out of the array, but then rewrites it into the array.
What strikes me, is that this line:
array[index] = array.pop();
Which is equivalent to this line when executed on the last element:
array[array.length - 1] = array.pop();
Could not have possibly resulted with the contents of array being equal to [ 0, 1, 2, 3, 4 ].
The way I see it, there are two options here:
The statement array.pop() is executed before the expression array.length - 1 is evaluated
The statement array.pop() is executed after the expression array.length - 1 is evaluated
In the first case, the contents of array would change as follows:
[ 0, 1, 2, 3, 4 ] // initial state
[ 0, 1, 2, 3 ] // after popping 4
[ 0, 1, 2, 4 ] // after assignment
In the second case, it should have triggered some sort of memory-access violation, because there would be an attempt to write into the 5th entry in the array (array[4]), when the length of the array is only 4 entries.
I know that NodeJS could be applying some memory-management scheme which would somehow allow this to complete without an "array index out of bound" exception, but I still don't get why it would let this operation "get away with it", and moreover, why the result is what it is.
Is the line array[array.length - 1] = array.pop() possibly undefined behavior?
Is there even such thing as undefined behavior in JavaScript?
The second option: The statement array.pop() is executed after the expression
array.length - 1 is evaluated
This is indeed what happens. JS evaluation is always left-to-right. It evaluates the array.length - 1 to index 4 of the array, then pops the last element from the array, then assigns that element to index 4.
In the second case, it should have triggered some sort of memory-access violation, because there would be an attempt to write into the 5th entry in the array (array[4]), when the length of the array is only 4 entries.
No, there are no memory access violations in JS. It just creates a new property again, and changes the length of the array accordingly. It's just the same as what happens with the code
const array = [0, 1, 2, 3, 4];
const element = array.pop();
console.log(JSON.stringify(array)); // [0,1,2,3]
array[4] = element;
console.log(JSON.stringify(array)); // [0,1,2,3,4]
Similarly, filling an array uses the same feature:
const array = [];
for (let i=0; i<5; i++)
array[i] = i; // no memory access violation
console.log(array.length); // 5
The last index results in [0, 1, 2, 3, <empty>, 4], not [ 0, 1, 2, 3, 4 ]:
function swapWithLastAndRemove(array, index) {
array[index] = array.pop();
}
var array = [0, 1, 2, 3, 4];
swapWithLastAndRemove(array, 5);
console.log(array);
Execution order isn't really something to worry about here, because the index is constant - the parameter index is never reassigned, so the number passed into swapWithLastAndRemove will always be the index, which the popped item is assigned to. Considering it introduces more confusion than necessary - easier to just keep in mind that the passed index is never reassigned.
What's going on is:
(1) The last item of the array is popped off (4), resulting in the array becoming [0, 1, 2, 3]
(2) The index passed in was 5, so array[5] is set to 4. The operation is equivalent to:
const arr = [0, 1, 2, 3];
arr[5] = 4;
console.log(arr);
This is resulting in a sparse array - there's no item at index 4 anymore. It's not undefined behavior, it's just weird, because sparse arrays are weird.
Is there even such thing as undefined behavior in Javascript?
Yes, see here.

JS - For Loops Pushing Array

I have an initial array,
I've been trying to change values (orders) by using pop, splice methods inside a for loop and finally I push this array to the container array.
However every time initial array is values are pushed. When I wrote console.log(initial) before push method, I can see initial array has been changed but it is not pushed to the container.
I also tried to slow down the process by using settimeout for push method but this didnt work. It is not slowing down. I guess this code is invoked immediately
I would like to learn what is going on here ? Why I have this kind of problem and what is the solution to get rid of that.
function trial(){
let schedulePattern = [];
let initial = [1,3,4,2];
for(let i = 0; i < 3; i++){
let temp = initial.pop();
initial.splice(1,0,temp);
console.log(initial);
schedulePattern.push(initial);
}
return schedulePattern;
}
**Console.log**
(4) [1, 2, 3, 4]
(4) [1, 4, 2, 3]
(4) [1, 3, 4, 2]
(3) [Array(4), Array(4), Array(4)]
0 : (4) [1, 3, 4, 2]
1 : (4) [1, 3, 4, 2]
2 : (4) [1, 3, 4, 2]
length : 3
When you push initial into schedulePattern, it's going to be a bunch of references to the same Array object. You can push a copy of the array instead if you want to preserve its current contents:
schedulePattern.push(initial.slice(0));
Good answer on reference types versus value types here: https://stackoverflow.com/a/13266769/119549
When you push the array to schedulepattern, you are passing a reference to it.
you have to "clone" the array.
use the slice function.
function trial(){
let schedulePattern = [];
let initial = [1,3,4,2];
for(let i = 0; i < 3; i++){
let temp = initial.pop();
initial.splice(1,0,temp);
console.log(initial);
schedulePattern.push(initial.slice());
}
return schedulePattern;
}
​
You have to know that arrays are mutable objects. What does it mean? It means what is happening to you, you are copying the reference of the object and modifying it.
const array = [1,2,3]
const copy = array;
copy.push(4);
console.log(array); // [1, 2, 3, 4]
console.log(copy); // [1, 2, 3, 4]
There are a lot of methods in Javascript which provide you the way you are looking for. In other words, create a new array copy to work properly without modify the root.
const array = [1,2,3]
const copy = Array.from(array);
copy.push(4);
console.log(array); // [1, 2, 3]
console.log(copy); // [1, 2, 3, 4]
I encourage you to take a look at Array methods to increase your knowledge to take the best decision about using the different options you have.

Newbie to Javascript: how to sum specific numbers in an array

Please help, I've been looking for an answer for far too long.
I'm trying to create an array using push method to insert the numbers
0 to 10 into positions 0 through 10 of the numbers array you just initialized above.
I did this:
var numbers = [];
for(var i = 0; i < 10; i++) {
numbers.push(i);
console.log(numbers);
And got this result, which I think is correct but not 100% sure:
[ 0 ]
[ 0, 1 ]
[ 0, 1, 2 ]
[ 0, 1, 2, 3 ]
[ 0, 1, 2, 3, 4 ]
[ 0, 1, 2, 3, 4, 5 ]
[ 0, 1, 2, 3, 4, 5, 6 ]
[ 0, 1, 2, 3, 4, 5, 6, 7 ]
[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
0
Then I am to test the array push method by printing the sum of the values at
position 3 and 6 of the array (use the console.log() function to print to the console).
The outputted value should be 9.
I am so stuck on this point and cannot find a sample anywhere of how to accomplish this. I thought it might be something like:
console.log(numbers(sum[3, 6]);
If you want to have a sum() function, then try the following:
function sum(x, y) {
return x + y;
}
console.log(sum(numbers[3], numbers[6]));
Here's a Fiddle: https://jsfiddle.net/7181h1ok/
To sum the values of two indices of an array, you use the + addition operator in the following fashion:
var numbers = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
var sum = numbers[3] + numbers[6]; //adds the value in index 3 of the numbers array to the value in index 6 of the numbers array.
console.log(sum); //prints the sum to the console.
As a note, if you are unfamiliar with JavaScript and/or its operators, there's useful documentation at w3schools that can get you started.
First, let's convert your code to a little bit better style:
const numbers = [];
for (let i = 0; i < 10; i++) {
numbers.push(i);
console.log(numbers);
}
Note: I made numbers a const instead of a var, since you don't change it. I also made i a let binding instead of a var. In general, var is a legacy and should never be used. Use const instead if at all possible, otherwise use let.
Also, I inserted a space after the for keyword. It is generally recommended to separate the parentheses which enclose the header of a control structure keyword (if, while, for, etc.) with a space, to make it visually distinct from the parentheses for the argument list of a function call, which has no space.
Secondly: Your result is not correct. (Hint: how many numbers are the numbers 0 to 10?) It should include the numbers 0 to 10, but it only includes the numbers 0 to 9. You have what is generally called an off-by-one-error. These errors are very common when dealing with trying to manage loop indices manually. This is the fix:
const numbers = [];
for (let i = 0; i <= 10; i++) {
// ↑
numbers.push(i);
console.log(numbers);
}
Most modern programming languages have better alternatives than dealing with loop indices manually in the form of higher-level abstractions such as iterators, maps, and folds. Unfortunately, ECMAScript doesn't have a Range datatype, otherwise this could simply be expressed as converting a Range to an Array.
If ECMAScript did have a Range datatype, it could for example look like one of these:
const numbers = Range(0, 10).toArray()
const numbers = Array.from(Range(0, 10))
Here is an alternative for creating the numbers Array that doesn't involve manually managing loop indices, but still requires knowing that 0 to 10 are 11 numbers:
const numbers = Array.from({length: 11}, (_, i) => i)
If you want to add the numbers at indices 3 and 6, you can simply dereference indices 3 and 6 and add the results:
console.log(numbers[3] + numbers[6])
In the comments, you asked how you would add up all numbers in the Array. Combining the elements of a collection using a binary operator is called a fold or reduce, and ECMAScript supports it out-of-the-box:
console.log(numbers.reduce((acc, el) => acc + el));
Note how there is no explicit loop, thus no explicit management of loop indices. It is simply impossible to make an off-by-one-error here.
It will be: console.log((+numbers[3]) + (+numbers[6]));
Typically, it should be console.log(numbers[3] + numbers[6]); but there's sometimes a issue that results in 36 instead of 9. The extra + signs tell javascript that it is a number.
NOTE: Remember that the first number is numbers[0]. The array starts with 0!

Is it safe to modify an array while looping it?

Is it safe to modify an array while looping it, such as push an element?
I'm using underscore each method
I recommend avoid each unless you absolutely need to cause a side effect for every item in a collection (trigger an event, print out a result, etc.). For simply modifying your collection, there are better ways.
If each added element is only the result of an individual input element, the typical functional pattern would be to flatmap the array, which can be thought of a two steps:
Using map to apply a function that for each element generates an array as a result. The overall result will be an array of arrays.
Using flatten on that array of arrays to get a one dimensional array.
Using underscore or lodash:
var origArray = [1, 2, 3, 4];
var duplicateIf3 = function (val) { return val === 3 ? [val, val] : val; };
_.flatten(origArray.map(duplicateIf3));
// -> [1, 2, 3, 3, 4]
(In typed FP, the function would have to return [val] for values that aren't 3, but flatten doesn't care--it flattens whatever you give it to one dimension.)
If the new element is dependent on all that came before it, you would probably use a reduce or fold instead, with the empty array as the initial value.
var origArray = [1, 2, 3, 4];
origArray.reduce(function (acc, val) { return acc.concat(acc).concat(val); }, []);
// -> [1, 1, 2, 1, 1, 2, 3, 1, 1, 2, 1, 1, 2, 3, 4]
(Sorry I couldn't think of a realistic example, but here every step uses the full output of all previous steps in a pretty simple way for illustrative purposes, and you can see from each value of the original array what's going on. Also note that you can make your own flatten from reduce, if you don't want to use underscore/lodash)
reduce is more general than flatmap, but both methods are able to convert an array into a larger array that some how depends on the first one.
If you're interested in learning more about this, I highly recommend checking out the free (online) Javascript Allongé, by Reg Braithwaite.
I'm not sure what you mean by safe, but you'd have to have a good reason to do it. I played around with it a bit, and here's what I got:
_.each(a, function(el) {
if (el % 2 == 0)
a.push(el + 1);
console.log(el);
});
// logs 1, 2, 3, 4, 5
console.log(a);
// logs [ 1, 2, 3, 4, 5, 3, 5 ]
In this case, there is no negative affect unless you wanted to go through the added elements, but you could find yourself in trickier situations if you're changing the specific elements:
_.each(a, function(el, index) {
a[index + 1] = el - 1;
if (el % 2 == 0)
a.push(el + 1);
console.log(el);
});
// logs 1, 0, -1, -2, -3
It would make more sense to use _.map for most use cases.

Categories

Resources