Sort Arrays By Their Length - javascript

I have the following arrays:
http://jsfiddle.net/3NZsK/
I need to sort the arrays by their length.
How to find out which one is the largest, the second largest, the third largest, and the least array?
For example, I have the following dummy function, that can get us the second largest array:
http://jsfiddle.net/WPRYf/
What is the above dummy function algorithm?
Thank you in advance

var
a = ["a", "a"];
b = ["b", "b", "b", "b"],
c = ["c", "c", "c"],
d = ["d"],
container = [a, b, c, d];
​container.sort(function (a, b) {
return b.length - a.length;
});
console.log(container);
container will be sorted from longest (most elements) to shortest (least number of elements). Access it as:
container[0] // longest
container[1] // 2nd longest
// ...
http://jsfiddle.net/pSRVq/

You can find the length of the array using the length method and then sort these according to their values to find the largest, second largest array.

Related

Why is sorting an array of strings different than sorting an array of numbers?

I can sort the array [1, 3, 2] by calling the .sort() method on it. Optionally, I can pass in a comparison function to do things like sorting the array in reverse. This always works when the array contains Numbers.
However, when the array contains Strings, the .sort() method is unpredictable. It will sort the array if no optional comparison function is provided, but will not sort if a comparison function is provided.
Why does this happen? Is there a workaround?
I have included a code snippet below that illustrates the problem.
let letters;
let numbers;
letters = ["a", "c", "b"];
numbers = [1, 3, 2];
console.log(letters.sort()); // ["a", "b", "c"] DOES SORT
console.log(numbers.sort()); // [1, 2, 3]. DOES SORT
letters = ["a", "c", "b"];
numbers = [1, 3, 2];
console.log(letters.sort((a, b) => a - b)); // ["a", "c", "b"] DOES NOT SORT
console.log(numbers.sort((a, b) => a - b)); // [1, 2, 3] DOES SORT
letters = ["a", "c", "b"];
numbers = [1, 3, 2];
console.log(letters.sort((a, b) => b - a)); // ["a", "c", "b"] DOES NOT SORT
console.log(numbers.sort((a, b) => b - a)); // [3, 2, 1] DOES SORT
Because "a" - "b" == NaN.
From Array.sort docs:
function compare(a, b) {
if (a is less than b by some ordering criterion) {
return -1;
}
if (a is greater than b by the ordering criterion) {
return 1;
}
// a must be equal to b
return 0;
}
Array.sort() expects the compare function to return a numerical value.
I would encourage you to read the documentation on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
Array sort is a weird one, but the gist of it is that the "compare function" (which is the thing you pass to sort() ie. (a, b) => a - b)) has to return a number.
You can easily test it in the browser console, and you'll see that subtracting two strings doesn't work 'a' - 'b'.
As a workaround you can use localeCompare (also mentioned in the docs).
var letters = ["a", "c", "b"];
letters.sort(function (a, b) {
return a.localeCompare(b);
});
"When sort() is called without arguments, the array items are transformed into strings and sorted. invoking the method on numbers performs the same alphabetical sorting:"
Not mine, though
https://dmitripavlutin.com/javascript-array-sort-numbers/.

Javascript delete object items leaves null traces [duplicate]

What is the difference between using the delete operator on the array element as opposed to using the Array.splice method?
For example:
myArray = ['a', 'b', 'c', 'd'];
delete myArray[1];
// or
myArray.splice (1, 1);
Why even have the splice method if I can delete array elements like I can with objects?
delete will delete the object property, but will not reindex the array or update its length. This makes it appears as if it is undefined:
> myArray = ['a', 'b', 'c', 'd']
["a", "b", "c", "d"]
> delete myArray[0]
true
> myArray[0]
undefined
Note that it is not in fact set to the value undefined, rather the property is removed from the array, making it appear undefined. The Chrome dev tools make this distinction clear by printing empty when logging the array.
> myArray[0]
undefined
> myArray
[empty, "b", "c", "d"]
myArray.splice(start, deleteCount) actually removes the element, reindexes the array, and changes its length.
> myArray = ['a', 'b', 'c', 'd']
["a", "b", "c", "d"]
> myArray.splice(0, 2)
["a", "b"]
> myArray
["c", "d"]
Array.remove() Method
John Resig, creator of jQuery created a very handy Array.remove method that I always use it in my projects.
// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
and here's some examples of how it could be used:
// Remove the second item from the array
array.remove(1);
// Remove the second-to-last item from the array
array.remove(-2);
// Remove the second and third items from the array
array.remove(1,2);
// Remove the last and second-to-last items from the array
array.remove(-2,-1);
John's website
Because delete only removes the object from the element in the array, the length of the array won't change. Splice removes the object and shortens the array.
The following code will display "a", "b", "undefined", "d"
myArray = ['a', 'b', 'c', 'd']; delete myArray[2];
for (var count = 0; count < myArray.length; count++) {
alert(myArray[count]);
}
Whereas this will display "a", "b", "d"
myArray = ['a', 'b', 'c', 'd']; myArray.splice(2,1);
for (var count = 0; count < myArray.length; count++) {
alert(myArray[count]);
}
I stumbled onto this question while trying to understand how to remove every occurrence of an element from an Array. Here's a comparison of splice and delete for removing every 'c' from the items Array.
var items = ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd'];
while (items.indexOf('c') !== -1) {
items.splice(items.indexOf('c'), 1);
}
console.log(items); // ["a", "b", "d", "a", "b", "d"]
items = ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd'];
while (items.indexOf('c') !== -1) {
delete items[items.indexOf('c')];
}
console.log(items); // ["a", "b", undefined, "d", "a", "b", undefined, "d"]
​
From Core JavaScript 1.5 Reference > Operators > Special Operators > delete Operator :
When you delete an array element, the
array length is not affected. For
example, if you delete a[3], a[4] is
still a[4] and a[3] is undefined. This
holds even if you delete the last
element of the array (delete
a[a.length-1]).
As stated many times above, using splice() seems like a perfect fit. Documentation at Mozilla:
The splice() method changes the content of an array by removing existing elements and/or adding new elements.
var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
myFish.splice(2, 0, 'drum');
// myFish is ["angel", "clown", "drum", "mandarin", "sturgeon"]
myFish.splice(2, 1);
// myFish is ["angel", "clown", "mandarin", "sturgeon"]
Syntax
array.splice(start)
array.splice(start, deleteCount)
array.splice(start, deleteCount, item1, item2, ...)
Parameters
start
Index at which to start changing the array. If greater than the length of the array, actual starting index will be set to the length of the array. If negative, will begin that many elements from the end.
deleteCount
An integer indicating the number of old array elements to remove. If deleteCount is 0, no elements are removed. In this case, you should specify at least one new element. If deleteCount is greater than the number of elements left in the array starting at start, then all of the elements through the end of the array will be deleted.
If deleteCount is omitted, deleteCount will be equal to (arr.length - start).
item1, item2, ...
The elements to add to the array, beginning at the start index. If you don't specify any elements, splice() will only remove elements from the array.
Return value
An array containing the deleted elements. If only one element is removed, an array of one element is returned. If no elements are removed, an empty array is returned.
[...]
splice will work with numeric indices.
whereas delete can be used against other kind of indices..
example:
delete myArray['text1'];
It's probably also worth mentioning that splice only works on arrays. (Object properties can't be relied on to follow a consistent order.)
To remove the key-value pair from an object, delete is actually what you want:
delete myObj.propName; // , or:
delete myObj["propName"]; // Equivalent.
delete Vs splice
when you delete an item from an array
var arr = [1,2,3,4]; delete arr[2]; //result [1, 2, 3:, 4]
console.log(arr)
when you splice
var arr = [1,2,3,4]; arr.splice(1,1); //result [1, 3, 4]
console.log(arr);
in case of delete the element is deleted but the index remains empty
while in case of splice element is deleted and the index of rest elements is reduced accordingly
delete acts like a non real world situation, it just removes the item, but the array length stays the same:
example from node terminal:
> var arr = ["a","b","c","d"];
> delete arr[2]
true
> arr
[ 'a', 'b', , 'd', 'e' ]
Here is a function to remove an item of an array by index, using slice(), it takes the arr as the first arg, and the index of the member you want to delete as the second argument. As you can see, it actually deletes the member of the array, and will reduce the array length by 1
function(arr,arrIndex){
return arr.slice(0,arrIndex).concat(arr.slice(arrIndex + 1));
}
What the function above does is take all the members up to the index, and all the members after the index , and concatenates them together, and returns the result.
Here is an example using the function above as a node module, seeing the terminal will be useful:
> var arr = ["a","b","c","d"]
> arr
[ 'a', 'b', 'c', 'd' ]
> arr.length
4
> var arrayRemoveIndex = require("./lib/array_remove_index");
> var newArray = arrayRemoveIndex(arr,arr.indexOf('c'))
> newArray
[ 'a', 'b', 'd' ] // c ya later
> newArray.length
3
please note that this will not work one array with dupes in it, because indexOf("c") will just get the first occurance, and only splice out and remove the first "c" it finds.
If you want to iterate a large array and selectively delete elements, it would be expensive to call splice() for every delete because splice() would have to re-index subsequent elements every time. Because arrays are associative in Javascript, it would be more efficient to delete the individual elements then re-index the array afterwards.
You can do it by building a new array. e.g
function reindexArray( array )
{
var result = [];
for( var key in array )
result.push( array[key] );
return result;
};
But I don't think you can modify the key values in the original array, which would be more efficient - it looks like you might have to create a new array.
Note that you don't need to check for the "undefined" entries as they don't actually exist and the for loop doesn't return them. It's an artifact of the array printing that displays them as undefined. They don't appear to exist in memory.
It would be nice if you could use something like slice() which would be quicker, but it does not re-index. Anyone know of a better way?
Actually, you can probably do it in place as follows which is probably more efficient, performance-wise:
reindexArray : function( array )
{
var index = 0; // The index where the element should be
for( var key in array ) // Iterate the array
{
if( parseInt( key ) !== index ) // If the element is out of sequence
{
array[index] = array[key]; // Move it to the correct, earlier position in the array
++index; // Update the index
}
}
array.splice( index ); // Remove any remaining elements (These will be duplicates of earlier items)
},
you can use something like this
var my_array = [1,2,3,4,5,6];
delete my_array[4];
console.log(my_array.filter(function(a){return typeof a !== 'undefined';})); // [1,2,3,4,6]
The difference can be seen by logging the length of each array after the delete operator and splice() method are applied. For example:
delete operator
var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
delete trees[3];
console.log(trees); // ["redwood", "bay", "cedar", empty, "maple"]
console.log(trees.length); // 5
The delete operator removes the element from the array, but the "placeholder" of the element still exists. oak has been removed but it still takes space in the array. Because of this, the length of the array remains 5.
splice() method
var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
trees.splice(3,1);
console.log(trees); // ["redwood", "bay", "cedar", "maple"]
console.log(trees.length); // 4
The splice() method completely removes the target value and the "placeholder" as well. oak has been removed as well as the space it used to occupy in the array. The length of the array is now 4.
Performance
There are already many nice answer about functional differences - so here I want to focus on performance. Today (2020.06.25) I perform tests for Chrome 83.0, Safari 13.1 and Firefox 77.0 for solutions mention in question and additionally from chosen answers
Conclusions
the splice (B) solution is fast for small and big arrays
the delete (A) solution is fastest for big and medium fast for small arrays
the filter (E) solution is fastest on Chrome and Firefox for small arrays (but slowest on Safari, and slow for big arrays)
solution D is quite slow
solution C not works for big arrays in Chrome and Safari
function C(arr, idx) {
var rest = arr.slice(idx + 1 || arr.length);
arr.length = idx < 0 ? arr.length + idx : idx;
arr.push.apply(arr, rest);
return arr;
}
// Crash test
let arr = [...'abcdefghij'.repeat(100000)]; // 1M elements
try {
C(arr,1)
} catch(e) {console.error(e.message)}
Details
I perform following tests for solutions
A
B
C
D
E (my)
for small array (4 elements) - you can run test HERE
for big array (1M elements) - you can run test HERE
function A(arr, idx) {
delete arr[idx];
return arr;
}
function B(arr, idx) {
arr.splice(idx,1);
return arr;
}
function C(arr, idx) {
var rest = arr.slice(idx + 1 || arr.length);
arr.length = idx < 0 ? arr.length + idx : idx;
arr.push.apply(arr, rest);
return arr;
}
function D(arr,idx){
return arr.slice(0,idx).concat(arr.slice(idx + 1));
}
function E(arr,idx) {
return arr.filter((a,i) => i !== idx);
}
myArray = ['a', 'b', 'c', 'd'];
[A,B,C,D,E].map(f => console.log(`${f.name} ${JSON.stringify(f([...myArray],1))}`));
This snippet only presents used solutions
Example results for Chrome
Why not just filter? I think it is the most clear way to consider the arrays in js.
myArray = myArray.filter(function(item){
return item.anProperty != whoShouldBeDeleted
});
They're different things that have different purposes.
splice is array-specific and, when used for deleting, removes entries from the array and moves all the previous entries up to fill the gap. (It can also be used to insert entries, or both at the same time.) splice will change the length of the array (assuming it's not a no-op call: theArray.splice(x, 0)).
delete is not array-specific; it's designed for use on objects: It removes a property (key/value pair) from the object you use it on. It only applies to arrays because standard (e.g., non-typed) arrays in JavaScript aren't really arrays at all*, they're objects with special handling for certain properties, such as those whose names are "array indexes" (which are defined as string names "...whose numeric value i is in the range +0 ≤ i < 2^32-1") and length. When you use delete to remove an array entry, all it does is remove the entry; it doesn't move other entries following it up to fill the gap, and so the array becomes "sparse" (has some entries missing entirely). It has no effect on length.
A couple of the current answers to this question incorrectly state that using delete "sets the entry to undefined". That's not correct. It removes the entry (property) entirely, leaving a gap.
Let's use some code to illustrate the differences:
console.log("Using `splice`:");
var a = ["a", "b", "c", "d", "e"];
console.log(a.length); // 5
a.splice(0, 1);
console.log(a.length); // 4
console.log(a[0]); // "b"
console.log("Using `delete`");
var a = ["a", "b", "c", "d", "e"];
console.log(a.length); // 5
delete a[0];
console.log(a.length); // still 5
console.log(a[0]); // undefined
console.log("0" in a); // false
console.log(a.hasOwnProperty(0)); // false
console.log("Setting to `undefined`");
var a = ["a", "b", "c", "d", "e"];
console.log(a.length); // 5
a[0] = undefined;
console.log(a.length); // still 5
console.log(a[0]); // undefined
console.log("0" in a); // true
console.log(a.hasOwnProperty(0)); // true
* (that's a post on my anemic little blog)
Others have already properly compared delete with splice.
Another interesting comparison is delete versus undefined: a deleted array item uses less memory than one that is just set to undefined;
For example, this code will not finish:
let y = 1;
let ary = [];
console.log("Fatal Error Coming Soon");
while (y < 4294967295)
{
ary.push(y);
ary[y] = undefined;
y += 1;
}
console(ary.length);
It produces this error:
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory.
So, as you can see undefined actually takes up heap memory.
However, if you also delete the ary-item (instead of just setting it to undefined), the code will slowly finish:
let x = 1;
let ary = [];
console.log("This will take a while, but it will eventually finish successfully.");
while (x < 4294967295)
{
ary.push(x);
ary[x] = undefined;
delete ary[x];
x += 1;
}
console.log(`Success, array-length: ${ary.length}.`);
These are extreme examples, but they make a point about delete that I haven't seen anyone mention anywhere.
function remove_array_value(array, value) {
var index = array.indexOf(value);
if (index >= 0) {
array.splice(index, 1);
reindex_array(array);
}
}
function reindex_array(array) {
var result = [];
for (var key in array) {
result.push(array[key]);
}
return result;
}
example:
var example_arr = ['apple', 'banana', 'lemon']; // length = 3
remove_array_value(example_arr, 'banana');
banana is deleted and array length = 2
Currently there are two ways to do this
using splice()
arrayObject.splice(index, 1);
using delete
delete arrayObject[index];
But I always suggest to use splice for array objects and delete for object attributes because delete does not update array length.
If you have small array you can use filter:
myArray = ['a', 'b', 'c', 'd'];
myArray = myArray.filter(x => x !== 'b');
I have two methods.
Simple one:
arr = arr.splice(index,1)
Second one:
arr = arr.filter((v,i)=>i!==index)
The advantage to the second one is you can remove a value (all, not just first instance like most)
arr = arr.filter((v,i)=>v!==value)
OK, imagine we have this array below:
const arr = [1, 2, 3, 4, 5];
Let's do delete first:
delete arr[1];
and this is the result:
[1, empty, 3, 4, 5];
empty! and let's get it:
arr[1]; //undefined
So means just the value deleted and it's undefined now, so length is the same, also it will return true...
Let's reset our array and do it with splice this time:
arr.splice(1, 1);
and this is the result this time:
[1, 3, 4, 5];
As you see the array length changed and arr[1] is 3 now...
Also this will return the deleted item in an Array which is [3] in this case...
Easiest way is probably
var myArray = ['a', 'b', 'c', 'd'];
delete myArray[1]; // ['a', undefined, 'c', 'd']. Then use lodash compact method to remove false, null, 0, "", undefined and NaN
myArray = _.compact(myArray); ['a', 'c', 'd'];
Hope this helps.
Reference: https://lodash.com/docs#compact
For those who wants to use Lodash can use:
myArray = _.without(myArray, itemToRemove)
Or as I use in Angular2
import { without } from 'lodash';
...
myArray = without(myArray, itemToRemove);
...
delete: delete will delete the object property, but will not reindex
the array or update its length. This makes it appears as if it is
undefined:
splice: actually removes the element, reindexes the array, and changes
its length.
Delete element from last
arrName.pop();
Delete element from first
arrName.shift();
Delete from middle
arrName.splice(starting index,number of element you wnt to delete);
Ex: arrName.splice(1,1);
Delete one element from last
arrName.splice(-1);
Delete by using array index number
delete arrName[1];
If the desired element to delete is in the middle (say we want to delete 'c', which its index is 1), you can use:
var arr = ['a','b','c'];
var indexToDelete = 1;
var newArray = arr.slice(0,indexToDelete).combine(arr.slice(indexToDelete+1, arr.length))
IndexOf accepts also a reference type. Suppose the following scenario:
var arr = [{item: 1}, {item: 2}, {item: 3}];
var found = find(2, 3); //pseudo code: will return [{item: 2}, {item:3}]
var l = found.length;
while(l--) {
var index = arr.indexOf(found[l])
arr.splice(index, 1);
}
console.log(arr.length); //1
Differently:
var item2 = findUnique(2); //will return {item: 2}
var l = arr.length;
var found = false;
while(!found && l--) {
found = arr[l] === item2;
}
console.log(l, arr[l]);// l is index, arr[l] is the item you look for
Keep it simple :-
When you delete any element in an array, it will delete the value of the position mentioned and makes it empty/undefined but the position exist in the array.
var arr = [1, 2, 3 , 4, 5];
function del() {
delete arr[3];
console.log(arr);
}
del(arr);
where as in splice prototype the arguments are as follows. //arr.splice(position to start the delete , no. of items to delete)
var arr = [1, 2, 3 , 4, 5];
function spl() {
arr.splice(0, 2);
// arr.splice(position to start the delete , no. of items to delete)
console.log(arr);
}
spl(arr);
function deleteFromArray(array, indexToDelete){
var remain = new Array();
for(var i in array){
if(array[i] == indexToDelete){
continue;
}
remain.push(array[i]);
}
return remain;
}
myArray = ['a', 'b', 'c', 'd'];
deleteFromArray(myArray , 0);
// result : myArray = ['b', 'c', 'd'];

Generate all possible combinations of variable-length comma-space-separated strings in JavaScript

Input:
["A", "B", "C"]
Expected output:
["A", "B", "C", "A, B", "A, C", "B, C", "A, B, C"]
This is a simple example case, but the function should work for strings and arrays of all lengths. Strings may have certain letters repeated, e.g. "AABB", which is distinct from "A" and "B". Order by number of elements first then alphanumerical sort is desired but not required for this solution.
You can use a permutation function, and then join certain strings after spliting them:
const arr = ["A", "B", "C"];
function getCombinations(chars) {
var result = [];
var f = function(prefix, chars) {
for (var i = 0; i < chars.length; i++) {
result.push(prefix + chars[i]);
f(prefix + chars[i], chars.slice(i + 1));
}
}
f('', chars);
return result;
}
const permutations = getCombinations(arr).map(e => e.length > 1 ? e.split("").join(", ") : e);
console.log(permutations);
Permutations function from this answer.
This is actually something I have been working on in scheme lately, what you appear to be looking for is a procedure that generates a power set from a set input. There just happens to be a recursive algorithm that already exists to solve this problem, but it is based on math that I don't need to go into here.
Here is a simple JavaScript implementation from another post here that I modified for your problem:
const myList = ["A", "B", "C"];
const powerSet =
theArray => theArray.reduce(
(subsets, value) => subsets.concat(
subsets.map(set => [value,...set])
),
[[]]
);
console.log(powerSet(myList));
This is what I ended up using as a basis for my implementation:
https://www.w3resource.com/javascript-exercises/javascript-function-exercise-21.php

Get first and last value from array

I have a an array being passed to me which is something like
var arr = ["A", "B", "C"];
What I am trying to achieve is to only get the first and last value so it should look like
var arr = ["A", "C"];
I'm not how to achieve this using splice because when I do
arr.splice(I am not sure what numbers to put here).forEach(function (element) {
console.log(element);
});
Can someone tell me how to achieve this please.
What I am trying to achieve is to only get the first and last value so
it should look like
Simply
arr.splice( 1, arr.length - 2 );
Demo
var arr = ["A", "B", "C"];
arr.splice(1, arr.length - 2);
console.log(arr);
For the first element you know arr[0]
For the last element arr[arr.length -1]
so let newAr = [arr[0], arr[arr.length -1]]
Considering you are trying to get values between the first and the last value of your array removed, you need to pass splice some value indicating how many elements your array contains.
this is why you should consider using:
var arr = ["A", "B", "C"];
arr.splice(1, arr.length - 2);
Explanation:
Splice takes at least 2 variables (this goes only if you use splice to remove items), the first being the position of the string at which you want to start removing items, and the second the number of items you actually want to remove.
To translate this simple line with words, it says
After the first element of the array, remove the next X items with X being the length of the array minus the first and the last element (this is why you have the "-2").
Hope i explained properly,
cheers
You can do
var arr = ["A", "B", "C"];
console.log(arr.filter((e, i) => i==0 || i==arr.length-1));

Generating 3D array from 2D array

I'm trying to create a music game where I have to generate a 3D array from a basic 2D array. The plan was to copy and paste the 2D array 4 times into the 3D array before modifying it, as shown:
var note3base = [
["C", "E", "G"],
["C#", "E#", "G#"],
["Db", "F", "Ab"],
["D", "F#", "A"],
["Eb", "G", "Bb"],
["E", "G#", "B"],
["F", "A", "C"],
["F#", "A#", "C#"],
["Gb", "Bb", "Db"],
["G", "B", "D"],
["Ab", "C", "Eb"],
["A", "C#", "E"],
["Bb", "D", "F"],
["B", "D#", "F#"],
["Cb", "Eb", "Gb"]
];
var note3 = new Array(4);
for (h=0;h<note3.length;h++){
note3[h] = note3base;
} //creates 4 copies of note3base in a 3d-array to be modified
for (i=0;i<note3[0].length;i++){
note3[1][i][1] = flat(note3[1][i][1]); //minor
note3[2][i][1] = flat(note3[2][i][1]);
note3[2][i][2] = flat(note3[2][i][2]); //dim
note3[3][i][2] = sharp(note3[3][i][2]); //aug
} //how did everything become the same?
The problem now seems to be that the for loop seems to apply the method to every single array (0 through 3).
The desired output for note3[0][1] would be C E G, note3[1][1] would be C Eb G, note[2][1] would be C Eb Gb, note[3][1] would be C E G#.
Any help is greatly appreciated!
I've included the (working) sharp and flat methods below for reference:
function sharp(note){
var newnote;
if (note.charAt(1) == "#"){
newnote = note.replace("#", "x");
} else if (note.charAt(1) == "b"){
newnote = note.replace("b", "");
} else {
newnote = note + "#";
}
return newnote;
}
function flat(note){
var newnote;
if (note.charAt(1) == "#"){
newnote = note.replace("#", "");
} else {
newnote = note + "b";
}
return newnote;
}
The problem is that when you assign a variable equal to an array like this:
someVar = someArray;
...it doesn't make a copy of the array, it creates a second reference to the same array. (This applies to all objects, and arrays are a type of object.) So after your loop, where you've said:
for (h=0;h<note3.length;h++){
note3[h] = note3base;
...all of the elements in note3 refer to the same underlying array.
To make an actual copy, you can manually copy all of the elements across using a loop, or you can use the .slice() method to make a copy for you:
for (h=0;h<note3.length;h++){
note3[h] = note3base.slice();
}
But that will only solve half of the problem, because note3base itself contains references to other arrays, and .slice() will just copy these references. That is, although note3[0] and note3[1] (and 2 and 3) will refer to different arrays, note3[0][0] and note3[1][0] and note3[2][0] and note3[3][0] will refer to the same ["C", "E", "G"] array. (And so forth.)
You need what's called a "deep copy". You could do it with a nested loop:
for (h=0;h<note3.length;h++){
// create this element as a new empty array:
note3[h] = [];
// for each 3-note array in note3base
for (var k = 0; k < note3base.length; k++) {
// make a copy with slice
note3[h][k] = note3base[k].slice();
}
}
Having said all that, I think an easier way to do the whole thing would be instead of having a note3base variable that refers to an array, make it a function that returns a new array:
function makeNote3Array() {
return [
["C", "E", "G"],
["C#", "E#", "G#"],
["Db", "F", "Ab"],
["D", "F#", "A"],
["Eb", "G", "Bb"],
["E", "G#", "B"],
["F", "A", "C"],
["F#", "A#", "C#"],
["Gb", "Bb", "Db"],
["G", "B", "D"],
["Ab", "C", "Eb"],
["A", "C#", "E"],
["Bb", "D", "F"],
["B", "D#", "F#"],
["Cb", "Eb", "Gb"]
];
}
Because the function uses an array literal it will create a brand new array of arrays every time it is called. So then you can do the following, with no need for .slice() or nested loops:
var note3 = new Array(4);
for (h=0;h<note3.length;h++){
note3[h] = makeNote3Array();
}
TL;DR, do this:
for (h=0;h<note3.length;h++){
note3[h] = note3base.slice(0);
}
Explanation:
The problem is coming from the difference between passing something'by value' and 'by reference' in Javascript.
When you assign a primitive value to a variable, like a = "string";, and then assign that to another variable, like b = a;, the value is passed to b 'by-value': its value is assigned to b, but b references a different part of memory. There are now two "string" values in the memory, one for a, and one for b.
a = "string";
b = a;
a = "gnirts";
console.log(b); // "string"
This is not how it works for non-primitive types, such as arrays. Here the value is passed to b 'by reference', meaning that there is still only one [1, 2, 3] array in the memory, and both a and b are pointing at it. This means that is you change an element in a, it will change for b as well, because they reference the same array in memory. So you get this:
a = [1, 2, 3];
b = a;
a[0] = "hello";
console.log(b); // ["hello", 2, 3]
b[0] has changed because it references the same location in memory as a[0]. To get around this problem, we need to explicitly make a copy of note3base when passing it to another variable, rather than just passing it by reference. We can do this with note3base.slice(0) as above.
Edit: read more here

Categories