I'm trying to learn how functions work in JS.
This function should order the string, but the result is equal to the string himself.
what do I do wrong?
a = "awbc"
function f(str) {
let temporary
for (i = 0; i < a.length; i++) {
for (j = 0; j < a.length; j++) {
if (a[j] < a[j + 1]) {
temporary = a[j]
a[j] = a[j + 1]
a[j + 1] = temporary
}
}
}
return a
}
console.log(f(a))
You need to use a replace method on array values. See this for reference:
Replace string in javascript array
Strings are immutable
As already pointed out by Pointy (edit: believe it or not, no pun intended) in the comments, strings are immutable and cannot be changed. But what you can do is create one separate string for each character in your string and put that in an array using the split() method. Then you can sort that array and when it is sorted use join() to create another string consisting of those characters in sorted order.
Your bubbleSort() implementation
First of all the algorithm you are trying to implement is called bubble sort and is one of the easy but unfortunately slow sorting algorithms as it takes O(nΒ²) in best, average and worst case runtime while good algorithms like merge sort only take O(n * log n).
Nevertheless, if you want to sort using bubbleSort() you need to make some changes to your code.
You are sorting in descending order therefore in every iteration of the outer loop the biggest value will be moved to the left-most position. Therefore in the next iteration you need to find the next biggest value and move it to the now left-most position. No need to check for all elements again as we already know that the left-most element is the biggest. Therefore start your inner loop at position i instead of 0. This will change nothing in the time complexity in big-O notation but will nevertheless improve the performance significantly.
Iteration i of outer loop
temporary
0
[ a,w,b,c ]
1
[ w,b,c,a ]
2
[ w,c,b,a ]
3
[ w,c,b,a ]
Also you are using a function in order to encapsulate functionality so you should not modify global variables within it. Use the str parameter you pass to it instead of a.
Last but not least you have this line temporary = a[j] but temporary should hold your array of individual character strings which you will destroy with this assignment. Create a new variable temp instead to do the swap.
Here an implementation of bubbleSort() with all those issues addressed.
/**
* Bubble sort algorithm which has sorts characters in a string in descending order.
* Best/ average and worst case runtime of bubble sort is O(nΒ²).
* As we iterate n times over (n - i) items.
* T(n) = Sum{from 0 to n}[n * (n-i)] = (n + 1) * n = nΒ² + n = O(nΒ²)
* #param {string} str string
* #returns
*/
function bubbleSort(str) {
const temporary = str.split("");
// temporary now contains every single character
console.log("After split:", temporary);
// in each iteration of the outer loop the "biggest" letter will be sorted to the front
for (let i = 0; i < temporary.length; i++) {
// you need to start from i as otherwise you will be moving already sorted letters (as they are moved to the front)
for (let j = i; j < temporary.length - 1; j++) {
if (temporary[j] < temporary[j + 1]) {
// you need other variable here, otherwise you will override temporary
const temp = temporary[j];
temporary[j] = temporary[j + 1];
temporary[j + 1] = temp;
}
}
}
// now join characters back together to a string
console.log("After sorting: ", temporary);
return temporary.join("");
}
console.log(bubbleSort("awbc"));
console.log(bubbleSort("another _string with &)8 w?ird chars"));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I'm looking at the following solution to this leetcode problem:
function SubstringsKDistinct(str, k) {
let start = 0;
let end = 0;
let result = [];
let len = str.length;
while (start < len && end < len) {
while (end <= len) {
if (str.slice(start, end).length > 1) {
let set = new Set(str.slice(start, end));
if (set.size == k) {
result.push(str.slice(start, end));
}
}
end++;
}
start++;
end = start;
}
return result.length;
}
At a glance it seems to me to be O(N^2) time complexity, considering we have one outer while loop where the number of operations is bound to the size of the input string, as well as another inner while loop with the number of operations bound to the size of the input string. In terms of space complexity I'm assuming O(N) because the size of the results array will also depend on size of the input string.
Could someone chime in here with thoughts? I'm relatively new to DS & Algos and trying to wrap my head around how to think about these operations during interviews.
The time complexity is worse than that due to this line inside the inner loop:
let set = new Set(str.slice(start, end));
When you pass an iterable to the Set constructor, it will iterate over all elements of the iterator and add them to the Set. So, if the sliced string is 10 characters long, that's 10 operations. This number is (on average, over all the loops) directly proportional to the number of characters in the input - so it's another O(n) operation inside the inner loop, making the whole inner loop O(n ^ 2), and the whole function O(n ^ 3) in terms of time complexity.
Space complexity is at least O(n ^ 2) because that's how many Sets are created.
I'm using javascript doing my homework. The question is required to find all the combination of room and meeting allocation with N rooms and n meetings.
For example, if I have 5 rooms and need to allocate to 3 meetings, the outcome will be something like
[1,1,3],[1,2,2],[1,3,1],[2,1,2],[2,2,1] and [3,1,1].
I need to use recursion to solve this question. But my recursion only gives me one outcome rather then all the outcomes.
function partition(num, m) {
if (m == 1) {
return num
} else {
for (i = 1; i < num; i++) {
return i + "," + partition(num - i, m - 1)
}
}
}
console.log(partition(5, 3))
How to list all the combinations with recursion? I'm struggling for a long time. Thank you very much.
Some issues:
Your code uses one global variable called i. This is not right, as the loop iteration in recursion will change the i that outer loops are using. Always declare your variables in a local scope. So for (let i.....)
Your function should not build a string through concatenation (+) and return a string, nor should it return a number in the base case, but it should return an array of arrays, just like you have depicted in the example output.
So the base case should return [[num]]. The outer array has just one element, which represents that there is just one partitioning possible, and the inner array specifies what that partitioning is: it just has one room.
Since the recursive call returns an array of arrays, you should iterate that recursive result, and add the current room assignment to form new combinations.
The iteration can stop a bit earlier than you have foreseen, since there must be enough "value" in num - i to fill up the remaining rooms with at least 1.
Here is a solution:
function partition(num, m) {
if (m == 1) {
return [[num]]; // return an array or arrays
} else {
let collect = []; // Prepare array for collecting the partitions
// Quit loop when not enough value to distribute in remaining rooms
for (let i = 1; i <= num - m + 1; i++) {
// Iterate the arrays that come back from recursion...
for (let arr of partition(num - i, m - 1)) {
collect.push([i, ...arr]); // ... and extend them.
}
}
return collect;
}
}
console.log(partition(5, 3));
It seems like you already know how to generate the sequences, so just describe the rules you used in your head. Then work the program backwards from there. Below we describe how to generate fixed-size combinations of size k from any array, t -
if the amount to choose, k, is zero, yield the empty combination, ()
(inductive) k is at least one. If the array t, is empty, there is nothing left to choose. Stop iteration
(inductive) k is at least one and the array has at least one element. Choose the first element of t and add it to each combination of the sub-problem, (t.slice(1), k - 1). And do not choose this element and yield from the sub-problem, (t.slice(1), k).
function* choosek(t, k) {
if (k == 0)
return (yield []) // 1
else if (t.length == 0)
return // 2
else {
// choose first element // 3
for (const c of choosek(t.slice(1), k - 1))
yield [t[0], ...c]
// skip first element
yield* choosek(t.slice(1), k)
}
}
for (const c of choosek(["π΄","π’","π΅","π‘","β«οΈ"], 3))
console.log(c.join(""))
π΄π’π΅
π΄π’π‘
π΄π’β«οΈ
π΄π΅π‘
π΄π΅β«οΈ
π΄π‘β«οΈ
π’π΅π‘
π’π΅β«οΈ
π’π‘β«οΈ
π΅π‘β«οΈ
A benefit of using an array as input instead of a number is we can generate fixed-sized combinations from any input, not just numerical ones. And because .slice also works on Strings, we can actually use string-based inputs too!
for (const c of choosek("ABCDE", 3))
console.log(c.join(""))
ABC
ABD
ABE
ACD
ACE
ADE
BCD
BCE
BDE
CDE
I have a question regarding javascript Math.random():
I have (for a game I'm building) to randomly generate every number from a given set (i.e. from 0 to 1000) and every time I have to generate a number, I have to check if that number has already been "generated".
The solution is pretty easy thinking about a simple algorithm that checks if the random integer is already present in the generated set. It loops generating numbers until it can't find it.
A snippet below:
/* ... */
for(var i = 0; i<upperBound; i++){
var randN = Math.floor(Math.random()*upperBound);
while(myRandomNumbers.contains(randN)){
loops++;
randN = Math.floor(Math.random()*upperBound);
}
myRandomNumbers.push(randN);
}
/* ... */
running example here
I'd like to know: is this the best way to achieve this? or are there any ways, instead of looping until it generates a "good" number, to exclude a particular set in the random generation?
Thanks a lot everyone!
Generate the set of numbers in order.
Sort the list randomly.
Here's an example using a naive, biased sort:
for (var nums=[],i=0;i<1000;++i) nums[i]=i+1;
nums.sort(function(){ return Math.random()-0.5 });
Then you can just pop() numbers off of nums to get the next 'random' number, guaranteed to never have been used before.
If your range of number is not prohibitively large, you could simply generate a list with all the numbers, randomise it, then pick it off one by one.
Here's a quick hack of your sample implementation to show this method in action: http://www.jsfiddle.net/ZTLt9/8/
I'd create an array and randomly shuffle it:
function shuffle(arr) {
var shuffled = arr.slice(0), i = arr.length, temp, index;
while (i--) {
index = Math.floor(i * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled;
}
// Create the array
var i = 1000, arr = [];
while (i--) arr[i] = i;
// Shuffle it
arr = shuffle(arr);
What about an array of booleans 1001 big.
When you want to check of the number has been generated, all you need to do is check the number at that position in the array:
if (!arr[randomnumber]){
arr[randomnumber] = true;
}
At the end, you can scan the array to find the numbers you need.
This has the added side effect of sorting your numbers, as the scan will pick them out in order.
Something similar to this was the subject of one of my blog posts:
http://www.jameswiseman.com/blog/2010/05/27/generate-and-sort-lottery-numbers/
The best way would probably be to generate an array of numbers, then shuffle it using the Fisher-Yates shuffle.
Here is the JavaScript example given in the Wikipedia article:
var n = a.length;
for(var i = n - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
(This assumes you have an array 'a' that contains the items you wish to shuffle.)
I have an array var words = []//lots of different words in it. I have a Math.floor(Math.random()*words.length) that chooses a random word from the array. This is run in a loop that runs for a random number of times (between 2 and 200 times). I would like to make sure that the random numbers do not get chosen more than once during the time that that loop runs. How would you suggest doing this?
There's multiple ways of doing this.
You can shuffle the entire collection, and just grab items from one end. This will ensure you won't encounter any one item more than once (or rather, more than the number of times it occured in the original input array) during one whole iteration.
This, however, requires you to either modify in-place the original collection, or to create a copy of it.
If you only intend to grab a few items, there might be a different way.
You can use a hash table or other type of dictionary, and just do a check if the item you picked at random in the original collection already exists in the dictionary. If it doesn't, add it to the dictionary and use it. If it already exists in the dictionary, pick again.
This approach uses storage proportional to the number of items you need to pick.
Also note that this second approach is a bit bad performance-wise when you get to the few last items in the list, as you can risk hunting for the items you still haven't picked for quite a number of iterations, so this is only a viable solution if the items you need to randomly pick are far fewer than the number of items in the collection.
There are several different approaches, which are more or less effective depending on how much data you have, and how many items you want to pick:
Remove items from the array once they have been picked.
Shuffle the array and get the first items.
Keep a list of picked items and compare against new picks.
Loop through the items and pick values randomly based on the probability to be picked.
I'd shuffle the array as follows and then iterate over the shuffled array. There's no expensive array splice calls and the shuffling routine consists of swapping two values in the array n times where n is the length of the array, so it should scale well:
function shuffle(arr) {
var shuffled = arr.slice(0), i = arr.length, temp, index;
while (i--) {
index = Math.floor(i * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled;
}
console.log(shuffle(["one", "two", "three", "four"]));
this is how you can do it without shuffling the whole array
a = "abcdefghijklmnopq".split("");
c = 0;
r = [];
do {
var len = a.length - c;
var rnd = Math.floor(Math.random() * len);
r.push(a[rnd]);
a[rnd] = a[len - 1];
} while(++c < 5);
console.log(r);
the idea is to pick from 0..(length - step) elements and then shift the picked element towards the end.
I'd try using a map (Object literal) instead of an Array with keys being indexes:
var words = [ /* ... */ ] , map = { } , length = words.length ;
for(var n = 0 ; n < length ; n++) map[n] = words[n] ;
then make a function to pick a random entry based on the length, delete the entry (hence the index) and adjust the length:
function pickRandomEntry() {
var random = Math.floor( Math.random() * length ) ;
var entry = map[random] ;
delete map[random] ;
length-- ;
return entry ;
}
with this approach, you have to check for an undefined return value (since random might return the same number) and run the function again until it returns an actual value; or, make an array of picked indexes to filter the random numbers (which will however slow performance in case of long iteration cycles).
HTH
There are several solutions to this.
What you could do is use .splice() on your array to remove the item which is hit by words.
Then you can iterate over your array until it's empty. If you need to keep the array pristine you can create a copy of it first and iterate over the copy.
var words = ['apple', 'banana', 'cocoa', 'dade', 'elephant'];
while (words.length > 0) {
var i = Math.floor(Math.random()*words.length);
var word = words.splice(i, 1)[0];
}
Or something to that effect.
Here is a way with prime numbers and modulo that seems to do the trick without moving the original array or adding a hash:
<html>
<head>
</head>
<body>
shuffle
<div id="res"></div>
<script>
function shuffle(words){
var l = words.length,i = 1,primes = [43,47,53,59,61,67,71,73,79],//add more if needed
prime = primes[parseInt(Math.random()*primes.length, 10)],
temp = [];
do{
temp.push((i * prime) % l);
}while(++i <= l);
console.log(temp.join(','));
console.log(temp.sort().join(','));
}
</script>
</body>
</html>
As a side result of testing some code I wrote a small function to compare the speed of using the array.push(value) method vs direct addressing array[n] = value. To my surprise the push method often showed to be faster especially in Firefox and sometimes in Chrome. Just out of curiosity: anyone has an explanation for it?
Here's the test (note: rewritten 2023/02/10)
const arrLen = 10_000;
const x = [...Array(10)].map( (_, i) => testArr(arrLen, i));
console.log(`Array length: ${arrLen}\n--------\n${x.join(`\n`)}`);
function testArr(n, action) {
let arr = [];
const perfStart = performance.now();
const methods =
` for (; n; n--) arr.push(n)
for (; i < n; i += 1) { arr[i] = i; }
for (; i < n; i += 1) arr.push(i)
while (--n) arr.push(n)
while (i++ < n) arr.push(n)
while (--n) arr.splice(0, 0, n)
while (--n) arr.unshift(n)
while (++i < n) arr.unshift(i)
while (--n) arr.splice(n - 1, 0, n)
while (n--) arr[n] = n`.split(`\n`).map(v => v.trim());
const report = i => `${methods[i]}: ${
(performance.now() - perfStart).toFixed(2)} milliseconds`;
let i = 0;
switch (action) {
case 0: for (; n; n--) arr.push(n)
case 1: for (; i < n; i += 1) { arr[i] = i; } break;
case 2: for (let i = 0; i < n; i += 1) arr.push(i); break;
case 3: while (--n) arr.push(n); break;
case 4: while (i++ < n) arr.push(n); break;
case 5: while (--n) arr.splice(0, 0, n); break;
case 6: while (--n) arr.unshift(n)
case 7: while (++i < n) arr.unshift(i); break;
case 8: while (--n) arr.splice(n - 1, 0, n); break;
default: while (n--) arr[n] = n;
}
return report(action);
}
.as-console-wrapper {
max-height: 100% !important;
}
All sorts of factors come into play, most JS implementations use a flat array that converts to sparse storage if it becomes necessary later on.
Basically the decision to become sparse is a heuristic based on what elements are being set, and how much space would be wasted in order to remain flat.
In your case you are setting the last element first, which means the JS engine will see an array that needs to have a length of n but only a single element. If n is large enough this will immediately make the array a sparse array -- in most engines this means that all subsequent insertions will take the slow sparse array case.
You should add an additional test in which you fill the array from index 0 to index n-1 -- it should be much, much faster.
In response to #Christoph and out of a desire to procrastinate, here's a description of how arrays are (generally) implemented in JS -- specifics vary from JS engine to JS engine but the general principle is the same.
All JS Objects (so not strings, numbers, true, false, undefined, or null) inherit from a base object type -- the exact implementation varies, it could be C++ inheritance, or manually in C (there are benefits to doing it in either way) -- the base Object type defines the default property access methods, eg.
interface Object {
put(propertyName, value)
get(propertyName)
private:
map properties; // a map (tree, hash table, whatever) from propertyName to value
}
This Object type handles all the standard property access logic, the prototype chain, etc.
Then the Array implementation becomes
interface Array : Object {
override put(propertyName, value)
override get(propertyName)
private:
map sparseStorage; // a map between integer indices and values
value[] flatStorage; // basically a native array of values with a 1:1
// correspondance between JS index and storage index
value length; // The `length` of the js array
}
Now when you create an Array in JS the engine creates something akin to the above data structure. When you insert an object into the Array instance the Array's put method checks to see if the property name is an integer (or can be converted into an integer, e.g. "121", "2341", etc.) between 0 and 2^32-1 (or possibly 2^31-1, i forget exactly). If it is not, then the put method is forwarded to the base Object implementation, and the standard [[Put]] logic is done. Otherwise the value is placed into the Array's own storage, if the data is sufficiently compact then the engine will use the flat array storage, in which case insertion (and retrieval) is just a standard array indexing operation, otherwise the engine will convert the array to sparse storage, and put/get use a map to get from propertyName to value location.
I'm honestly not sure if any JS engine currently converts from sparse to flat storage after that conversion occurs.
Anyhoo, that's a fairly high level overview of what happens and leaves out a number of the more icky details, but that's the general implementation pattern. The specifics of how the additional storage, and how put/get are dispatched differs from engine to engine -- but this is the clearest i can really describe the design/implementation.
A minor addition point, while the ES spec refers to propertyName as a string JS engines tend to specialise on integer lookups as well, so someObject[someInteger] will not convert the integer to a string if you're looking at an object that has integer properties eg. Array, String, and DOM types (NodeLists, etc).
These are the result I get with your test
on Safari:
Array.push(n) 1,000,000 values: 0.124
sec
Array[n .. 0] = value
(descending) 1,000,000 values: 3.697
sec
Array[0 .. n] = value (ascending)
1,000,000 values: 0.073 sec
on FireFox:
Array.push(n) 1,000,000 values: 0.075 sec
Array[n .. 0] = value (descending) 1,000,000 values: 1.193 sec
Array[0 .. n] = value (ascending) 1,000,000 values: 0.055 sec
on IE7:
Array.push(n) 1,000,000 values: 2.828 sec
Array[n .. 0] = value (descending) 1,000,000 values: 1.141 sec
Array[0 .. n] = value (ascending) 1,000,000 values: 7.984 sec
According to your test the push method seems to be better on IE7 (huge difference), and since on the other browsers the difference is small, it seems to be the push method really the best way to add element to an array.
But I created another simple test script to check what method is fast to append values to an array, the results really surprised me, using Array.length seems to be much faster compared to using Array.push, so I really don't know what to say or think anymore, I'm clueless.
BTW: on my IE7 your script stops and browsers asks me if I want to let it go on (you know the typical IE message that says: "Stop runnign this script? ...")
I would recoomend to reduce a little the loops.
push() is a special case of the more general [[Put]] and therefore can be further optimized:
When calling [[Put]] on an array object, the argument has to be converted to an unsigned integer first because all property names - including array indices - are strings. Then it has to be compared to the length property of the array in order to determine whether or not the length has to be increased. When pushing, no such conversion or comparison has to take place: Just use the current length as array index and increase it.
Of course there are other things which will affect the runtime, eg calling push() should be slower than calling [[Put]] via [] because the prototype chain has to be checked for the former.
As olliej pointed out: actual ECMAScript implementations will optimize the conversion away, ie for numeric property names, no conversion from string to uint is done but just a simple type check. The basic assumption should still hold, though its impact will be less than I originally assumed.
array[n] = value, when previously initialised with a length (like new Array(n)), is faster than array.push, when ascending when n >= 90.
From inspecting the javascript source code of your page, your Array[0 .. n] = value (ascending) test does not initialize the array with a length in advance.
So Array.push(n) sometimes comes ahead on the first run, but on subsequent runs of your test then Array[0 .. n] = value (ascending) actually consistently performs best (in both Safari and Chrome).
If the code is modified so it initialises an array with a length in advance like var array = new Array(n) then Array[0 .. n] = value (ascending) shows that array[n] = value performs 4.5x to 9x faster than Array.push(n) in my rudimentary running of this specific test code.
This is consistent with other tests, like #Timo KΓ€hkΓΆnen reported. See specifically this revision of the test he mentioned: https://jsperf.com/push-method-vs-setting-via-key/10
The modified code, so you may see how I edited it and initialised the array in a fair manner (not unnecessarily initialising it with a length for the array.push test case):
function testArr(n, doPush){
var now = new Date().getTime(),
duration,
report = ['<b>.push(n)</b>',
'<b>.splice(0,0,n)</b>',
'<b>.splice(n-1,0,n)</b>',
'<b>[0 .. n] = value</b> (ascending)',
'<b>[n .. 0] = value</b> (descending)'];
doPush = doPush || 5;
if (doPush === 1) {
var arr = [];
while (--n) {
arr.push(n);
}
} else if (doPush === 2) {
var arr = [];
while (--n) {
arr.splice(0,0,n);
}
} else if (doPush === 3) {
var arr = [];
while (--n) {
arr.splice(n-1,0,n);
}
} else if (doPush === 4) {
var arr = new Array(n);
for (var i = 0;i<n;i++) {
arr[i] = i;
}
} else {
while (--n) {
var arr = [];
arr[n] = n;
}
}
/*console.log(report[doPush-1] + '...'+ arr.length || 'nopes');*/
duration = ((new Date().getTime() - now)/1000);
$('zebradinges').innerHTML += '<br>Array'+report[doPush-1]+' 1.000.000 values: '+duration+' sec' ;
arr = null;
}
Push adds it to the end, while array[n] has to go through the array to find the right spot. Probably depends on browser and its way to handle arrays.