Constructive bubble sort not working as expected - javascript

I'm trying to create a simple bubble sort in JavaScript and cannot understand why my code is not working, the problem seems to be coming from the second if statement, I do not know the exact problem as the browser I'm testing it in refuses to load the page when using this code.
var arr = [4, 6, 0, 3, -2, 1];
var arr2 = [arr[0]];
arr.forEach(function(elem){
for(var j=0; j<arr2.length; j++){
if(elem < arr2[j]){
arr2.splice(j, 0, elem);
break;
}
//if number is largest on last iteration add it to the end of the array
if(j == arr2.length-1){
console.log(elem);
//problem seems to be here
arr2[arr2.length] = elem;
}
}
});
console.log(arr);
console.log(arr2);

You're adding elem to the array with .splice if it is lower than arr[j] but you never removing the old instance of elem. Conesquently, you're always adding items array and it is always getting larger. You need to take out the old instance of elem from the array so the array length remains constant from iteration to iteration.
So, somewhere, you should be passing 1 to the second argument of splice to take something out of the array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice

Related

Trying to understand why my function won't remove more than one instance of the value arguments[i] from newArr [duplicate]

This question already has answers here:
How to filter an array from all elements of another array
(24 answers)
Closed 8 months ago.
function destroyer(arr) {
const newArr = [...arguments[0]]
for(let i = 0; i < newArr.length; i++){
for(let j = 1; j < arguments.length; j++){
if(arguments[j] == newArr[i]){
newArr.splice(i,1)
console.log(newArr)
}
}
}
}
destroyer([3, 5, 1, 2, 2], 3, 5, 2);
New JS learner here.
Working on a problem that is supposed to look through the first arg in destroyer which will be an array and remove the elements that match the arguments following the array.
Results are [1,2] in the console output. Intended results are [1] with the given parameters Upon further testing it seems like the destroyer function is only removing the first instance of any value that it matches in newArr. If I take the second instance of '2' out of the test set it behaves as intended. I'm trying to understand what in my logic here is wrong. I've tried several different iteration patterns and can't seem to see what the problem is.
Thanks for any help!
I am going to link a few other answers here is this seems to be a fairly common question. The gist is that you are iterating over the live array while removing items from that array resulting in potentially skipping items.
How to iterate over an array and remove elements in JavaScript
Remove multiple elements from array in Javascript/jQuery
Alternatively you could also the filter method to help remove multiple values:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Try this:
function destroyer(arr) {
const newArr = [...arguments[0]]
for(let i = newArr.length - 1; i >= 0; i--){
for(let j = 1; j < arguments.length; j++){
if(arguments[j] == newArr[i]){
newArr.splice(i,1)
console.log(newArr)
}
}
}
}
This is an array pointer issue.
when i = 3 and j = 3, the function will match arguments[3] == newArr[3].
At this moment, newArr will be removed 1 element which is the first 2, and then the newArr becomes an new array that is [3,5,1,2].
The next index i is 4 which doesn't exist in the new newArr. So, the function will return and finish. That's why you get [3,5,1,2].
function destroyer (arg) {
if(!Array.isArray(arg)) return arg;
return arg.filter(x => !Array.prototype.slice.call(arguments, 1).includes(x))
}
It is because newArr is getting shorter as you splice it through your loops and the loop itself is also shortened.
see this probably your solution

Why does the pop() method stop working when used within a map method?

I want to reverse an array in place without using reverse method. I tried the following code:
function reverseArray(arr) {
return arr.map(() => {
return arr.pop()
})
}
console.log(reverseArray([1, 2, 3, 4]));
//logs [4,3,undefined,undefined]
Using dev tools debugger, this is what logs in the console: (4) [4, 3, empty × 2].
arr.pop() returns undefined if called on an empty array but it shouldn't be empty in this case, to my understanding.
Question: what's happening? Why are the first two elements printing correctly and the rest aren't?
You're removing elements from the array while you're iterating over it. The documentation says:
The range of elements processed by map is set before the first invocation of callback. ... Elements that are deleted after the call to map begins and before being visited are not visited.
So map() determines at the beginning that it will iterate 4 times, creating an array with 4 elements.
On the first iteration, arr.pop() removes arr[3]. On the second iteration it removes arr[2].
The third iteration expects to process arr[2]. But since that element doesn't exist any more, it doesn't call the callback function, and just stores undefined in the result array. The same thing happens on the fourth iteration.
You could iterate over a copy of the array.
function reverseArray(arr) {
return [...arr].map(() => {
return arr.pop()
})
}
console.log(reverseArray([1, 2, 3, 4]));
The problem is, that you pop some elements and therefore, they don't exist in the array anymore. So, the first two elements are poped and two other ones are written in those spots, but internally they don't exist anymore in the mean time.
There are also many other (better) solutions like this one (from here):
function reverse (array) {
var i = 0,
n = array.length,
middle = Math.floor(n / 2),
temp = null;
for (; i < middle; i += 1) {
temp = array[i];
array[i] = array[n - 1 - i];
array[n - 1 - i] = temp;
}
}

Why does splice() behave weird?

I am trying to flatten multiple arrays into one and remove duplicate elements in the array for an exercise from FCC.
Specifically, the splice method shows one thing in the console but acts differently. Can anyone tell me why splice() is not deleting the the duplicates that I have identified with the nested loop?
function uniteUnique(arr) {
var arr1 = arguments;
newArr= [];
for(var i=0; i<arr1.length; i++){
for(var l=0; l<arr1[i].length; l++){
newArr.push(arr1[i][l]);
}
}
console.log(newArr);
for(var t=0; t<newArr.length; t++){
for(var p=0; p<newArr.length; p++){
if(newArr[t]===newArr[p+1]){
console.log("without splice ", newArr[p+1]);
console.log("with splice ", newArr.splice(newArr[p+1],1))
newArr.splice(newArr[p+1],1);
}
}
}
return newArr;
}
console.log(uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]));
Will output [4,1].
But should output [1,3,2,5,4]
first Iteration Log:
without splice 1
with splice [3]
Second:
without splice 1
with splice [2]
Third:
without splice 2
with splice [4]
Fourth:
without splice 1
with splice [2]
You have three errors here:
There's no need to use p+1 here. Just use p.
splice expects an index as its first argument, but you are passing the value being considered for removal as an index to perform the removal. This is doesn't make sense. Instead of .splice(newArr[p], 1) you need .splice(p, 1).
You do not stop a value from being considered against itself as a potential duplicate. Your if condition must also include the condition ... && p!=t since newArr[t]===newArr[p] will always be (uselessly) true in case that t equals p
Modifying an array in its loop is something is not recommended. If you don't have to use splice, just create a brand new array and push values by searching and if not already exists in it.
function uniteUnique() {
var newArr = [];
for(var i=0; i<arguments.length; i++) {
var arr = arguments[i];
for(var j=0; j<arr.length; j++) {
if(newArr.indexOf(arr[j]) == -1) {
newArr.push(arr[j]);
}
}
}
return newArr;
}
console.log(uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]));
In respect to the removing duplicates "in place" of your approach, splice is working fine and your algorithm is almost good, try to debug through each step of the iterations after the flattening, to remove the duplicates.
You are looping n squared times (because p = t and you iterate p * t times).
But, something is not doing what you want to, have a look at these:
if(newArr[t]===newArr[p+1]){ // HERE IT WON'T DO WHAT YOU WANT
I'm not going to give away the answer unless you mention it, I'm just trying to demonstrate that the problem does not rely with splice itself, rather than your algorithm.
The other line that you would need to modify would be this one
newArr.splice(newArr[p+1],1);
Basically with a few changes in these two lines, you would reach the goal to remove the duplicates.

When looping through values of a JS array, and I remove value, do I need to use while instead of for?

var myArray = [1,2,3,4,5,6,7,8,9];
function isOdd(value){
return value % 2;
}
for(var i = 0; i < myArray.length; i++){
if(isOdd(myArray[i])){
myArray.splice(i,1);
i--;
}
}
The code above takes an array of arbitrary length and checks each value. If the value of the bit of the array meets an arbitrary condition (in this case if it is odd), then it is removed from the array.
Array.prototype.splice() is used to remove the value from the array, and then i is decremented to account for the rest of the values in the array "moving down" to fill in the gap that the removed value left (so the loop doesn't skip over a value).
However, the for loop ends when i equals the length of the array, which gets shorter as values are removed.
Does the value of myArray.length decrease dynamically as the loop proceeds, or does it save the value at the start of the loop and not update as values are removed? If the latter, what can I do to fix my loop?
Thank you!
myArray.length is changing with the operation on the array. But looping and splicing leads to unwanted results, if not proper padded.
To prevent unnecessary corrections, use a while loop from the end, to keep the rest of the array for processing.
function isOdd(value) {
return value % 2;
}
var myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9],
i = myArray.length;
while (i--) {
if (isOdd(myArray[i])) {
myArray.splice(i, 1);
}
}
console.log(myArray);
The length property is read in every iteration, and the splice method does update its value, so it works as you would expect. However, I would say that this is not a good coding practice, a while loop is much more readable, so it should be the obvious choice.
To answer the question directly: you don't have to use while instead of for, but you definitely should.
Use Array.filter instead
var myArray = [1,2,3,4,5,6,7,8,9];
myArray=myArray.filter(function(item,index) {
return !(item % 2);
})
console.log(myArray)
This is where you'd want to use Array.prototype.filter() if you don't absolutely HAVE to modify the original array, in-place.
As you suspect, the .length property of the array is being updated every time you splice(). The filter() method was built for exactly this sort of operation.
var myArray = [1,2,3,4,5,6,7,8,9];
function isOdd(value){
return value % 2;
}
var filteredArray = myArray.filter(function(item){
return !isOdd(item);
});
console.log(filteredArray);
A more concise version of the above code:
var myArray = [1,2,3,4,5,6,7,8,9];
function isEven(value){
return value % 2 === 0;
}
var filteredArray = myArray.filter(isEven);
console.log(filteredArray);
An even more concise version relying on ES6 arrow syntax:
var myArray = [1,2,3,4,5,6,7,8,9];
var isEven = value => value % 2 === 0;
var filteredArray = myArray.filter(isEven);
console.log(filteredArray);
And, in the event that you absolutely MUST edit the array in-place / use splice() here, I would recommend using Array.prototype.forEach() over a for or while loop. forEach() is another higher order method that allows you to realize the same functionality with less boilerplate. As with most higher order methods/functions, it allows you to focus on defining what you need to do rather than exactly how it needs to be done.
var myArray = [1,2,3,4,5,6,7,8,9];
function isOdd(value){
return value % 2;
}
myArray.forEach(function(c, i, a){
if(isOdd(c)){
a.splice(i,1);
}
})
console.log(myArray);
You can use both of them and it's depends on which one you like. if you prefer to use while loop then Nina's answer looks good and if you want to use for loop then consider to manage counter changes by yourself completely or when the length changes:
function isOdd(value) {
return value % 2;
}
var arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (var i = 0; i < arr1.length;)
isOdd(arr1[i]) ? arr1.splice(i, 1) : i++;
console.log(arr1);
var arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (var i = 0; i < arr2.length; i++)
if (isOdd(arr2[i])) {
arr2.splice(i, 1);
i--;
}
console.log(arr2);

In javascript, how do I remove an element from an array of objects?

In javascript, how do I remove an element from an array of objects?
Here is the code:
$.fn.mapImage.deletePinpoint = function(image, pinpoint){
var deleted = false;
for (var i = 0; i < image.pinpoints.length; i++) {
if(image.pinpoints[i].position == pinpoint.position){
image.pinpoints.remove(i);
deleted = true;
}
if(deleted){
image.pinpoints[i].position -= 1;
}
}
$('.edit-mode').find('div.dynamic-pinpoint-area').remove();
$('.edit-mode').find('div.pinpoint-text').remove();
$('.create-mode').find('div.static-pinpoint-area').remove();
$('.create-mode').find('div.pinpoint-text').remove();
$.fn.mapImage.load(image);
}
image.pinpoints is the array of objects. Thanks again guys!
See https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/delete_Operator
e.g. (from source)
var trees = ["redwood","bay","cedar","oak","maple"];
delete trees[3];
if (3 in trees) {
// this does not get executed
}
.splice is the method given at w3schools.com http://www.w3schools.com/jsref/jsref_splice.asp
To remove one element from an array with index x you would have trees.splice(x,x+1); This removes x and returns it if you need it.
I think you should rephrase the question to be more clear. From your example, it looks like multiple elements can get deleted from the image.pinpoints array if the position property matches that of pinpoint. So it will delete each image.pinpoints[i].position == pinpoint.position where i goes from 0 to (image.pinpoints.length - 1).
Since you are also iterating through the array at the same time, I wouldn't recommend using splice by itself. Instead delete each index first, and then cleanup the array in a second pass.
splice and delete will work differently as delete creates a hole in the array and sets the deleted property's value to undefined. On the other hand, splice will remove the element as if it never existed, and fix the indexes of all elements after it to be contiguous. Consider this example:
> var a = [2,3,5,7,11]; // create an array of 5 elements
> undefined
> a[2] // see the value of the third element
> 5
> delete a[2] // delete the third element using "delete"
> true
> a // log contents of a
> [2, 3, undefined, 7, 11]
> a[2] // index 2 still exists with value "undefined" now
> undefined
splice here by itself is also problematic as if you delete an element, all indexes after that element will shift one up, and you will skip checking the next element. Consider this second example:
> var a = [2,3,5,7,11]; // create array of 5 elements
> for(var i = 0; i < a.length; i++) {
if(a[i] == 3 || a[i] == 5) { // if it's 3 or 5, take it out
a.splice(i, 1);
}
}
> a
[2, 5, 7, 11]; // yikes, 5 still exists
In the above example, 5 is still present as we never checked that value. When we saw the 3, the current index was 1. After splicing the array, the next element - 5 moved up to take it's spot and became index 1. Since we're already done with index 1 at this point, we will simply move onto the next index - 2, which now has value 7, and skip 5. In general it's not a good practice to iterate using indexes, and do in-place removals.
As a solution, I would create a new array and only insert the properties which are not to be deleted in it.
$.fn.mapImage.deletePinpoint = function(image, pinpoint) {
// will hold all objects that are not to be deleted
var remainingPinpoints = [];
for (var i = 0; i < image.pinpoints.length; i++) {
// reverse condition
if(image.pinpoints[i].position != pinpoint.position) {
// add to new array
remainingPinpoints.push(image.pinpoints[i]);
}
}
// assign new array to pinpoints property
image.pinpoints = remainingPinpoints;
...
}

Categories

Resources