In an interview I was asked the following question.
I am given two arrays, both of them are sorted.
BUT
Array 1 will have few -1's and Array 2 will have total numbers as the total number of -1's in Array 1.
So in the below example
array1 has three -1's hence array2 has 3 numbers.
let say
var arrayOne = [3,6,-1,11,15,-1,23,34,-1,42];
var arrayTwo = [1,9,28];
Both of the arrays will be sorted.
Now I have to write a program that will merge arrayTwo in arrayOne by replacing -1's, and arrayOne should be in sorted order.
So the output will be
arrayOne = [ 1,3, 6, 9, 11, 15, 23, 28 ,34, 42 ]
Sorting should be done without use of any sort API.
I have written a following code
function puzzle01() {
var arrayOne = [3, 6, -1, 11, 15, -1, 23, 34, -1, 42];
var arrayTwo = [1, 9, 28];
var array1Counter = 0,
isMerged = false;
console.log(" array-1 ", arrayOne);
console.log(" array-2 ", arrayTwo);
for (var array2Counter = 0; array2Counter < arrayTwo.length; array2Counter++) {
isMerged = false;
while (isMerged === false && array1Counter < arrayOne.length) {
if (arrayOne[array1Counter] === -1) {
arrayOne[array1Counter] = arrayTwo[array2Counter];
isMerged = true;
}
array1Counter++;
}
} //for
console.log(" array-1 + array-2 ", arrayOne);
bubbleSort(arrayOne);
console.log(" Sorted array ", arrayOne);
} //puzzle01
puzzle01();
// implementation of bubble sort for sorting the
// merged array
function bubbleSort(arrayOne) {
var nextPointer = 0,
temp = 0,
hasSwapped = false;
do {
hasSwapped = false;
for (var x = 0; x < arrayOne.length; x++) {
nextPointer = x + 1;
if (nextPointer < arrayOne.length && arrayOne[x] > arrayOne[nextPointer]) {
temp = arrayOne[x];
arrayOne[x] = arrayOne[nextPointer];
arrayOne[nextPointer] = temp;
hasSwapped = true;
}
} //for
} while (hasSwapped === true);
} // bubbleSort
The output of the above code is
array-1 [ 3, 6, -1, 11, 15, -1, 23, 34, -1, 42 ]
array-2 [ 1, 9, 28 ]
array-1 + array-2 [ 3, 6, 1, 11, 15, 9, 23, 34, 28, 42 ]
Sorted array [ 1, 3, 6, 9, 11, 15, 23, 28, 34, 42 ]
From the above code you can see, I have first merged the two arrays and than sorted the final one.
Just wanted to know,
Is there a better solution.
Is there any flaw in my solution.
Please let me know, it will be helpfull.
After reading all your very helpful comments and answers, I found was able to figure out a more faster solution.
Let us take an example
var arrayOne = [3,6,-1,11,15,-1,32,34,-1,42,-1];
var arrayTwo = [1,10,17,56],
Step1: I will iterate through arrayTwo. Take the next element (i.e. '1') and compare with next element of arrayOne (i.e. '3') and compare.
step 2a : If element of array1 is greater than element of array2 than swap array elements. Now go to next element of array1.
OR
step 2b : If element of array1 is equal to -1 than swap array elements. Now go to next element of array2.
step 3: Go to step 1.
So
in the above example
first iteration,
array1 = [1,6,-1,11,...]
array2 = [3,10,17,56]
second iteration,
array1 = [1,3,-1,11,..]
array2 = [6,10,17,56]
third iteration,
array1 = [1,3,6,11..]
array2 = [-1,10,17,56]
fourth iteration
array1 = [1,3,6,10,..]
array2 = [-1,11,17,56]
and so on.
at the end I will get the output
array1 = [ 1, 3, 6, 10, 11, 15, 17, 32, 34, 42, 56 ]
array2 = [-1,-1,-1]
Please find the code below,
function puzzle02(arrayOne,arrayTwo){
var array1Counter = 0,
array2Counter = 0,
hasMinusOneOccurred = false;
console.log(" array-1 ",arrayOne);
console.log(" array-2 ",arrayTwo);
while(array2Counter < arrayTwo.length){ // iterate through array2
do{
if(arrayOne[array1Counter] === -1){ // if -1 occurred in array1
hasMinusOneOccurred = true;
// swaping numbers at current position of array1
// with current position of array2
swap(arrayOne,arrayTwo,array1Counter,array2Counter);
// recheck if the current value is greater than other values
// of array1
if(recheckAndSort(arrayOne,array1Counter) === true){
array1Counter = -1;// recheck array1 from start
}else{
// recheck the current array1 counter, for doing so go 1 count back
// so that even if the counter is incremented it points to current
// number itself
array1Counter--;
}
}else if(arrayOne[array1Counter] > arrayTwo[array2Counter]){
swap(arrayOne,arrayTwo,array1Counter,array2Counter);
}else{
array1Counter++;
continue;
}
array1Counter++;
}while(hasMinusOneOccurred === false); // end of do-while
array2Counter++;
hasMinusOneOccurred = false;
}//end of while
console.log(" Sorted array ",arrayOne);
function swap(arr1,arr2,arr1Index,arr2Index){
var temp = arr2[arr2Index];
arr2[arr2Index] = arr1[arr1Index];
arr1[arr1Index] = temp;
}// end of swap
// this method is call if -1 occures in array1
function recheckAndSort(arrayOne,array1Counter){
var isGreaterVal = true,
prevCounter = 0,
nextCounter = 0,
temp = 0,
recheckFromStart = false;
if(array1Counter === 0){ // if -1 occurred at first position of array1.
// flag to check if -1 occurrec at first position
// if yes, iterate array1 from start
recheckFromStart = true;
// iterate forward to check wether any numbers are less than current position,
// if yes than move forward
for(var j = 0; isGreaterVal; j++){
nextCounter = j + 1;
if(arrayOne[nextCounter] === -1){
// swaping numbers of array1 between next to current
swap(arrayOne,arrayOne,nextCounter,j);
isGreaterVal = true;
}else if(arrayOne[nextCounter] < arrayOne[j]){
// swaping numbers of array1 between next to current
swap(arrayOne,arrayOne,nextCounter,j);
isGreaterVal = true;
}else{
isGreaterVal = false;
}
}//end of for
}else{// if -1 occurred in the middle position of array1 and is been swaped then
// iterate backwards to check if any number less then current position exists,
// if yes than shift backwards.
for(var i = array1Counter; isGreaterVal; i--){
prevCounter = i - 1;
if(arrayOne[prevCounter] > arrayOne[i]){
// swaping numbers of array1 between previous to current
swap(arrayOne,arrayOne,prevCounter,i);
isGreaterVal = true;
}else{
isGreaterVal = false;
}
}// end of for
}
return recheckFromStart;
}// end of recheckAndSort
} // end of puzzle02
After calling the above function
puzzle02([3,6,-1,11,15,-1,32,34,-1,42,-1],[1,10,17,56]);
The output of above code is,
array-1 [ 3, 6, -1, 11, 15, -1, 32, 34, -1, 42, -1 ]
array-2 [ 1, 10, 17, 56 ]
Sorted array [ 1, 3, 6, 10, 11, 15, 17, 32, 34, 42, 56 ]
Thanks.
You can try following approach:
Logic
Create a new array that will be returned.
Check for first element in arrayTwo and keep it in a variable say val.
Loop over arrayOne and check if current value is greater than val, push it in array and decrement value of i by 1 to check next value as well with current element.
Now check for current element. If it is less than 0, ignore it, else push value to array.
Return this array.
function mergeAndSort(a1, a2) {
var matchCount = 0;
var ret = [];
for (var i = 0; i < a1.length; i++) {
var val = a2[matchCount];
if (a1[i] > val) {
ret.push(val)
matchCount++
i--;
continue;
}
if (a1[i] > 0) {
ret.push(a1[i]);
}
}
console.log(ret.join())
return ret;
}
var arrayOne = [3, 6, -1, 11, 15, -1, 23, 34, -1, 42]
var arrayTwo = [7, 19, 38];
var arrayThree = [1, 9, 28];
var arrayFour = [1,2,5]
mergeAndSort(arrayOne, arrayTwo)
mergeAndSort(arrayOne, arrayThree)
mergeAndSort(arrayOne, arrayFour)
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}
Note: Not putting check for number of elements in arrayTwo as its clearly mentioned in question that it will be same.
There is a clean O(N) in-place solution.
First "pack" arrayOne by moving all -1 (-- below) to the front. This takes a single backward pass.
Then perform a merge by iteratively moving the smallest element among arrayTwo and the tail of arrayOne and overwriting the next --. The gap will narrow down but there will always remain room for the elements of arrayTwo.
3, 6, --, 11, 15, --, 23, 34, --, 42
1, 9, 28
Packing:
3, 6, --, 11, 15, --, 23, 34, --, 42
3, 6, --, 11, 15, --, 23, --, 34, 42
3, 6, --, 11, 15, --, --, 23, 34, 42
3, 6, --, 11, --, --, 15, 23, 34, 42
3, 6, --, --, --, 11, 15, 23, 34, 42
3, --, --, --, 6, 11, 15, 23, 34, 42
--, --, --, 3, 6, 11, 15, 23, 34, 42
Merging:
--, --, --, 3, 6, 11, 15, 23, 34, 42
1, 9, 28
1, --, --, 3, 6, 11, 15, 23, 34, 42
--, 9, 28
1, 3, --, --, 6, 11, 15, 23, 34, 42
--, 9, 28
1, 3, 6, --, --, 11, 15, 23, 34, 42
--, 9, 28
1, 3, 6, 9, --, 11, 15, 23, 34, 42
--, --, 28
1, 3, 6, 9, 11, --, 15, 23, 34, 42
--, --, 28
1, 3, 6, 9, 11, 15, --, 23, 34, 42
--, --, 28
1, 3, 6, 9, 11, 15, 23, --, 34, 42
--, --, 28
1, 3, 6, 9, 11, 15, 23, 28, 34, 42
--, --, --
You should do something like insertion sort. As both the arrays are sorted (except -1s), the smallest number in array2 will be placed somewhere between first element and the first -1, 2nd element of array2 will be placed somewhere anywhere after the 1st -1 in array1 and before or at the 2nd -1 in array1 and so on.
So you have to insert a particular element of array2 in only a segment of array1 and not the whole array. Also each element of array2 have to consider different segment or subarray of array1. So, effective time complexity of this logic will be O(n+m) where n is the length of array1 and m is the length of array2.
Couldn't it be something like
compare each item of arrayTwo with each item of arrayOne
If it comes to bigger that that of arrayOne, insert the item and while iterating arrayOne delete all the -1 .
That's actually an interesting question. There are many sorting algorithms, and the most efficient ones always starts from one "unchangeable" array, so without changing the values inside that. Yet here your goal is to change the value when it encounters -1, so that the value is taken from the second array.
So, you need a sorting algorithm that doesn't divide the array in pieces because if the last element of your second array is 1 (the lowest), it has to be moved to the start. If you're using a sorting algorithm that breaks the array in pieces (the divide-and-conquer tactic like quick sort) or that uses recursion, it can be problematic because it cannot be moved to the start of your main array. Unless you are aware of the main array.
What you need is an algorithm that performs a step-by-step algorithm.
The algorithm that I've used is a bubble sort, which checks each element step by step. It's then easier to replace the value if it's -1 and move its position correctly to the array. However, it is not so efficient. Maybe I will edit my post to see if I can improve that.
function mergeAndSort(arr1, arrMergeIn) {
// merge + sort using arr1 as large one
var mergeIndex = 0;
for (var i = 0; i < arr1.length; ++i) {
if (arr1[i] === -1) arr1[i] = arrMergeIn[mergeIndex++];
var j = i;
while (j > 0 && arr1[j - 1] > arr1[j]) {
var tmp = arr1[j - 1];
arr1[j - 1] = arr1[j];
arr1[j] = tmp;
j--
}
}
return arr1;
}
// one liner console output
function showArray(arr) {
console.log(arr.join(','));
}
showArray(mergeAndSort([3, 6, -1, 11, 15, -1, 23, 34, -1, 42], [7, 19, 38]));
showArray(mergeAndSort([3, 36, -1, 1, 10, -1, 9, 34, -1, 42], [17, 9, 38]));
showArray(mergeAndSort([3, 36, -1, 1, 10, -1, 9, 34, -1, 42], [17, 9, 1]));
showArray(mergeAndSort([-1, 36, -1, 1, 10, -1, 9, 34, -1, 42], [17, 9, 100, 1]));
showArray(mergeAndSort([-1, -1, 1, 100, -1, 9, 34, -1], [17, 9, 9, 1]));
Or, you can use another strategy: replace the "-1" elements with the elements from that another array and perform an efficient algorithm on that. Although in worst-case scenario's, the "-1"s are at the end of the array, which means that there is an ~N operation + additional average complexity of a sorting algorithm (the efficient ones are of ~N*log(N))
You could iterate arrayOne in a single loop and then arrayTwo.
The idea is to separate the target index from the actual index. The first loop ignores -1 and and keep the target index.
If an actual value is greater then the first value of arrayTwo, both values swapped and in arrayTwo takes a sorting place by iterating and swapping with grater values.
Then the actual item is assigned to the target index.
Both indices gets incremented.
At the end all items of arrayTwo are added to arrayOne.
function order(arrayOne, arrayTwo) {
var i = 0, j, l = 0;
while (i < arrayOne.length) {
if (arrayOne[i] === -1) {
i++;
continue;
}
if (arrayTwo[0] < arrayOne[i]) {
[arrayOne[i], arrayTwo[0]] = [arrayTwo[0], arrayOne[i]];
j = 0;
while (arrayTwo[j] > arrayTwo[j + 1]) {
[arrayTwo[j], arrayTwo[j + 1]] = [arrayTwo[j + 1], arrayTwo[j]];
j++;
}
}
arrayOne[l++] = arrayOne[i++];
}
j = 0;
while (l < arrayOne.length) {
arrayOne[l++] = arrayTwo[j++];
}
return arrayOne;
}
console.log(order([3, 6, -1, 11, 15, -1, 23, 34, -1, 42], [7, 19, 38]));
console.log(order([3, 6, -1, 11, 15, -1, 23, 34, -1, 42], [1, 9, 28]));
console.log(order([3, 6, -1, 11, 15, -1, 23, 34, -1, 42], [1, 2, 5]));
console.log(order([3, 6, -1, 11, 15, -1, 23, 34, -1, 42], [43, 44, 45]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is a simple and compact implementation of the merge sort algorithm. There will only be the same amount of operations as there are elements.
I achieve this by creating an array with the same number of elements as both arrays combined, then iterating that array.
Upon each iteration:
If both arrays have elements (and the smallest element is not -1) it will move the smallest element into the result.
If only one of the arrays still has elements (and the next element is not -1), it will move the first element from that array into the result.
Finally assign the result to the first array as per your spec
const mergeSort = (a, b) => Object.assign(a,
new Int32Array(a.length + b.length).reduce(m => {
const el = [
[a, b][+!(a[0] <= b[0])],
[a, b][+!a.length]][+!(a.length && b.length)
].shift()
if(el !== -1) m.push(el)
return m
}, [])
)
const arr1 = [1,3,5,7,9]
const arr2 = [0,2,4]
mergeSort(arr1, arr2)
console.log(arr1) // [0,1,2,3,4,5,7,9]
Let me re-phrase three most important aspects of the task:
Both arrays are sorted and include positive integers (except -1 place-holders in Array1)
The -1 is meaningless placeholder (its position in Array1 is random)
Array1 length equals output array length
Pseudo logic:
parse both array1 and array2 from the first position to end of array
ignore -1 values in array1
if array1[current position] <= array2[current position] then write array1[current position] into merged array and move to next position in array1; otherwise apply same for array2
Code example:
public static void AMerge()
{
int[] array1 = new int[] { 3, 6, -1, 11, 15, -1, 23, 34, -1, 42 };
int[] array2 = new int[] { 1, 9, 28 };
int[] arrayMerged = new int[array1.Length];
int array1Index = 0;
int array2Index = 0;
for (int arrayMergedIndex = 0; arrayMergedIndex < array1.Length; arrayMergedIndex++)
{
while (array1[array1Index] == -1) array1Index++; //ignore -1 placeholders
if ((array1Index < array1.Length && array2Index >= array2.Length)
|| array1[array1Index] <= array2[array2Index]) //choose which array will provide current merged value
{
arrayMerged[arrayMergedIndex] = array1[array1Index];
array1Index++;
}
else
{
arrayMerged[arrayMergedIndex] = array2[array2Index];
array2Index++;
}
}
char[] charsToTrim = { ',', ' '};
string arrayMergedString = "{";
foreach (int f in arrayMerged) arrayMergedString += f.ToString() + " ";
arrayMergedString = arrayMergedString.TrimEnd(charsToTrim) + "}";
Console.WriteLine(arrayMergedString);
Console.ReadKey();
}
}
Note:
Optimizing for speed => creating new arrayMerged; optimization for space would require moving array1 elements in the else branch
You should use merge function from Merge sort, and modify it so that it doesn't create a new array, but instead uses array1, and perform translation, after insertion of an element from array2, that will shift elements to the right until the next -1, and thus overwrite the -1.
function merger(a1, a2) {
var i = 0;
var j = 0;
while (i < a1.length && j < a2.length) {
if (a1[i] > a2[j]) {
// Swap values
var temp = a2[j];
a2[j] = a1[i];
a1[i] = temp;
i++;
} else if (a1[i] !== -1 && a1[i] <= a2[j]) {
i++;
} else {
var temp = a2[j];
a2[j] = a1[i];
a1[i] = temp;
i++;
j++;
}
}
return a1;
}
var arrayOne = [3, 5, -1, 11, 15, -1, 23, 34, -1, 42];
var arrayTwo = [6, 19, 38];
console.log(merger(arrayOne, arrayTwo))
With certain pre-conditions (no other numbers with <0, a1 should be smaller than a2 etc - which could all be handled) this should solve the problem in JS.
Since they are both sorted, the order of arrayTwo's items should match the order of -1s in arrayOne. Then the job becomes simple and can be implemented as follows;
function replaceMissing(a,b){
var i = 0;
return a.map(n => n < 0 ? b[i++] : n);
}
var arrayOne = [3,6,-1,11,15,-1,23,34,-1,42],
arrayTwo = [7,19,38];
result = replaceMissing(arrayOne,arrayTwo);
console.log(result);
Edit: I believe the upper solution does make more sense in the general logic of the question. If the position of -1s does not mean anything then what use do they have? Let's just delete the -1's and do a simple insertion of arrayTwo items at proper indices in arrayOne. This can very simply be done as follows.
function replaceMissing(a,b){
var j = b.length-1;
return b.concat(a.reduceRight((r,m,i) => (m < 0 ? r.splice(i,1)
: m < b[j] && r.splice(i+1,0,b.splice(j--,1)[0]),
r), a.slice()));
}
var arrayOne = [3,6,-1,11,15,-1,23,34,-1,42],
arrayTwo = [1,25,38];
result = replaceMissing(arrayOne,arrayTwo);
console.log(result);
Related
Write a function squareWave(arr) that takes in the following array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], and starts replacing the numbers, one by one, with zeroes, until it reaches a multiple of 5. From that point onwards, start replacing the numbers with 1s, until you reach the next multiple of 5.
Then, from that point onwards, start replacing with 0s again, then 1s again,and so on until you reach the end of the array.
My code is not working Anybody can help me?
let input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
function squareWave(arr) {
let zeros = true;
let output = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] % 5) {
arr[i] = 0;
} else if (arr[i] !== 5) {
arr[i] = 1;
}
}
console.log(arr)
}
Output should be=[0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1]
You are not keeping track if the current entry should be a 0 or 1. Also, you are not using variables zeros and output
Instead of a boolean, you can keep a 0 or 1 in the variable zeros and flip the value when the mod of 5 equals zero.
if (arr[i] % 5 === 0) {
Then for every iteration write the value of zeros
let input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
function squareWave(arr) {
let zeros = 0;
let output = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] % 5 === 0) {
zeros = 1 - zeros
}
output[i] = zeros;
}
return output;
}
console.log(squareWave(input));
Or in short:
let input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
let res = input.map(i => Math.abs(Math.floor(i / 5) % 2))
console.log(res)
You can use Array's map function.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
let control = 0;
const list = input.map(value => {
if (value % 5 === 0) {
control = control === 0 ? 1 : 0;
}
return control;
});
console.log(list);
As this is clearly a homework question, I won't give you the answer, but suggest your next steps. You actually almost have the idea of the answer, but you didn't actually implement it.
I'm assuming your current code output looks like [0, 0, 0, 0, 1, 0, 0, 0, 0, 1, ...]
At the beginning of your code you have a boolean variable called zeros. What do you think this boolean is for, and why haven't you used it in your loop?
So your current code is outputting 1 when the value is a multiple of 5, but then it forgets immediately after. How might you fix that?
To replace an element in an array:
Use the indexOf() method to get the index of the element.
Use bracket notation to change the value of the element at the specific index.
The value of the array element will get updated in place.
const arr = ['a', 'b', 'c'];
const index = arr.indexOf('a'); // it will give you 0
if (index !== -1) {
arr[index] = 'z';
}
console.log(arr); // it will give you ['z', 'b', 'c']
I have two array
const array1 = [12, 13, 14, 15, 16, 17, 18, 19, 20]
const array2 = [17, 18]
I want to return first three element from array1 after comparing the index of array2 17 element or first element with the array1.
desired O/P from array1 after comparing is [14, 15, 16]
i have tried getting the index of the particular element.
const indexOf17FromArray1 = array1.indexOf(array2[0]) //5
just do array.slice((idx - 3), idx)
You can combine the use of array methods indexOf and slice.
First determine the last index (like you already have). Then based on your desired length modify it for use with slice. slice takes the start and end indices.
const array1 = [12, 13, 14, 15, 16, 17, 18, 19, 20];
const array2 = [17, 18];
const length = 3;
const indexOf17FromArray1 = array1.indexOf(array2[0])
console.log(array1.slice(indexOf17FromArray1 - length, indexOf17FromArray1));
You could get the index and check if -1 or subtract an offset and get a new array.
const
array = [12, 13, 14, 15, 16, 17, 18, 19, 20],
value = 17,
index = array.indexOf(value),
result = index === -1
? []
: array.slice(Math.max(index - 3, 0), index);
console.log(result);
Get the index of the element you want as reference.
const index = array1.indexOf(array2[0]);
If you want 3 elements after this index:
array1.slice(index + 1, index + 4);
If you want 3 elements before this index:
array1.slice(index - 3, index);
Finding the index of element = 17 in array1:
const array1 = [12, 13, 14, 15, 16, 17, 18, 19, 20];
const array2 = [17, 18];
const position17 = array1.findIndex(e => e === array2[0]);
The method findIndex will loop through array1 and check if the current element(e) is equal to array2[0].
Creating an array only with the three elements prior to element 17:
Here you will use the filter method, I'm passing the current value(item) and it's index. This method will check if the index is below "positon17" and if it is whitin the first three elements prior to element 17.
const newArray = array1.filter((item, index)=> (index < position17 & index >=position17-3)&& item);
console.log(newArray);
Here is a very simple function that will give you the next three after
your supplied index.
const array = [1,2,3,4,5,6,7,8,9];
const index = 2;
const threeElementsAfterIndex = [];
for( let i = 0; i < array.length; i++ ) {
if(i > index && i < index + 3) {
threeElementsAfterIndex.push(array[i]);
}
}
return threeElementsAfterIndex;
const array = [1, 2, 3, 4, 5, 6, 7, 8];
const index = 2;
console.log(array.slice(index + 1, index + 4));
I am trying to remove even integers from an array. The function I wrote is still returning an index with an even number in it. I'm not sure why I'm getting that kind of behavior. Any thoughts?
let arr = [6, 3, 19, 43, 12, 66, 43];
const removeEvenValues = arr => {
arr.forEach((num, i) => {
if(num % 2 === 0) arr.splice(i, 1);
});
};
removeEvenValues(arr); // arr becomes [3, 19, 43, 66, 43] but should become [3, 19, 43, 43]
Don't splice an array while iterating over it - that'll change it in place. For example, if you start with an array [2, 4, 6], and on the first iteration, remove the item at index 0, the array will then be [4, 6], and you'll go onto the second iteration, where i is 1. You'll then remove the item at the 1st index, which is 6, resulting in the array becoming [4].
Every time an item gets removed, your current method forgets to re-check the new item that fell into the index of the removed item.
It's very confusing. Either use a for loop and start at the end of the array instead, or (even better) use .filter:
let arr = [6, 3, 19, 43, 12, 66, 43];
const removeEvenValues = arr => arr.filter(num => num % 2 !== 0);
console.log(removeEvenValues(arr));
If you have to mutate the existing array (which usually isn't a good idea unless absolutely necessary), then:
let arr = [6, 3, 19, 43, 12, 66, 43];
const removeEvenValues = arr => {
for (let i = arr.length - 1; i >=0; i--) {
if (arr[i] % 2 === 0) {
arr.splice(i, 1);
}
}
return arr;
}
console.log(removeEvenValues(arr));
Given the array (list) of integers below, I am looking to extract each integer and if three or more integers ascend consecutively, I want to replace the middle integers with a "-" to represent a range. And then lastly return values as a string.
For example, the first 7 integers from list: -6, -3, -2, -1, 0, 1, 3
Would become '-6,-3-1,3'
Because there is more than three consecutive integers from -3 to 1.
Ultimately, solution(list) should return the following string: "-6,-3-1,3-5,7-11,14,15,17-20"
In its present form it returns the following string: "-6,-3,-2,-1,0,1,3,4,5,7,8,9,10,11,14,15,17,18,19,20"
Which is simply the array converted into a string.
var list = [-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20];
solution(list);
function solution(list) {
final = [];
range = [];
while (list.length > 0) {
take = list.splice(0,1);
range.push(take);
n = 1;
while (take+n === list[0]) {
a = list.splice(0,1);
range.push(a);
n++;
}
if (range.length >= 3) {
min = Math.min(range).toString();
max = Math.max(range).toString();
final.push(min + "-" + max);
range.length = 0;
} else if (range.length === 2) {
final.push(range[0].toString());
final.push(range[1].toString());
range.length = 0;
} else if (range.length === 1) {
final.push(range[0].toString());
range.length = 0;
}
}
return final.join(",");
}
However, I was able to get the desired result successfully in Ruby:
list = [-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
def solution(list)
final = []
range = []
while (list.length > 0) do
take = list.shift
range << take
n = 1
while (take+n == list[0]) do
a = list.slice!(0)
range << a
n +=1
end
if (range.length >= 3)
final << (range.min.to_s + "-" + range.max.to_s)
range = []
elsif (range.length == 2)
final << range[0]
final << range[1]
range = []
elsif (range.length == 1)
final << range[0].to_s
range = []
end
end
return final.join(",")
end
My approach in Ruby is almost identical that of my JavaScript. So, if I was wondering if someone could:
1) Explain why this approach works with Ruby, but not Javascript. Please feel free to inform me even if it is a simple syntax error on my part.
2) How I could possibly go about extracting the integers and returning the correct ranges in JavaScript?
Your help is greatly appreciated! Thanks!
Here's a better ruby solution:
list = [-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
stringified = list.chunk_while{|a, b| a == b - 1}.map do |seq|
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the core of the solution
if seq.length > 2 # long enough sequence
"#{seq.first}-#{seq.last}"
else
seq
end
end.join(', ')
stringified # => "-6, -3-1, 3-5, 7-11, 14, 15, 17-20"
This should also be a hint for your javascript implementation. Separate concerns as much as possible. Chunking, stringifying, turning a sequence into a range-like string: these all should be separate pieces of code. If they're not all tangled together, they're much easier to reason about.
In Javascript, you could use a three pass approach, first for getting the grouped ranges, then get the ranges in an array and later join the whole array.
var array = [-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20],
result = array
.reduce(function (r, a) {
var last = r[r.length - 1];
if (last && last[1] + 1 === a) {
last[1] = a;
} else {
r.push([a, a]);
}
return r;
}, [])
.reduce(function (r, a) {
return r.concat(a[0] === a[1] ? a[0] : a[1] - a[0] < 2 ? a : a.join('-'));
}, [])
.join();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Splice returns an array, you have to take the first item from returned array
take = list.splice(0,1)[0];
Also note that shift (take = list.shift()) is a better candidate for this operation.
Math.min doesn't accept arrays, you can use a workaround with apply/call
Math.min.apply(null,range);
In ES6 this can be done as Math.min(...range) using spread syntax
It is better to clear an array by assigning to a new array.
range = [];
I obviously prefer the functional and declarative method by #ninasholz This is just to explain why my code didn't work
var list = [-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20];
console.log(solution(list));
function solution(list) {
final = [];
range = [];
while (list.length > 0) {
//splice returns array take first item
take = list.splice(0,1)[0];
range.push(take);
n = 1;
while (take + n === list[0]) {
a = list.splice(0,1)[0];
range.push(a);
n++;
}
console.log(range);
if (range.length >= 3) {
//Math.min doesnt accept arrays
min = Math.min.apply(null,range);
max = Math.max.apply(null,range);
final.push(min + "-" + max);
range = [];
} else if (range.length === 2) {
final.push(range[0]);
final.push(range[1]);
range = [];
} else if (range.length === 1) {
final.push(range[0]);
range = [];
}
}
return final.join(",");
}
I cannot figure out the best way to dynamically generate a multidimensional array with 2 different sizes.
We have a UI that requires a row of 4 items, then 3. This pattern would repeat until the content in the array has been spent.
This is essentially what I need to do:
// Convert
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
// to
const rows [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10, 11], [12, 13, 14]];
This is what I currently have, it is only converting the arrays to 4 each.
const buildRows = (arr, length) => arr.reduce((rows, val, i) => (
i % length == 0 ? rows.push([val]) : rows[rows.length-1].push(val)
) && rows, []);
Thank you in advance for the help.
The following solution will mutate (empty) the input array array:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
let result = [], i = 0;
while(array.length) { // while there still items in array (array is continually getting shrunk untill it is emptied (array.length === 0))
result.push(array.splice(0, i++ % 2? 3: 4)); // cut the first 3 or 4 numbers depending on the index of the cut i (if i is pair, then cut 4, else, cut 3) and then push the cut-out items into the result array
}
console.log(result);
If you don't want to mutate it, then use slice instead of splice, but you'll have to provide the start index of the cut:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
let result = [], i = 0, next = 0; // next will be the index from which the cut will start
while(next < array.length) { // there is still items to cut
let itemsToCut = i % 2? 3: 4; // determine how many items we are going to cut
result.push(array.slice(next, next + itemsToCut)); // cut the items between next and next + itemsToCut
next += itemsToCut; // increment next by the number of cut-out items so it will point to the next item
i++;
}
console.log(result);
I suggest a more self-documenting generator solution where even & odd row-sizes are not hardcoded but supplied via arguments:
function* reshape(array, ...rows) {
let i = 0;
while (true) for (let row of rows) {
if (i >= array.length) return;
yield array.slice(i, i += row);
}
}
// Example:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
for (let row of reshape(array, 4, 3)) console.log(row);
A true generator purist would simplify reshape by first introducing a repeat generator:
function* repeat(iterable) {
while (true) yield* iterable;
}
function* reshape(array, ...rows) {
let i = 0;
for (let row of repeat(rows)) {
if (i >= array.length) break;
yield array.slice(i, i += row);
}
}
// Example:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
for (let row of reshape(array, 4, 3)) console.log(row);
You can achieve that using Array#reduce, a pointer to the last place, and a step variable that alternates between 3 and 4:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
let last = 0;
let step = 0;
const result = array.reduce((r, num, i) => {
if(i === last + step) { // when the previous sub array is full
r.push([]); // add another sub array
last = i; // mark the start index of the current sub array
step = step === 4 ? 3 : 4; // alternate the step
}
r[r.length - 1].push(num); // push the number to the last sub array
return r;
}, []);
console.log(result);
Straighfoward and easy-to-read solution:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
const r = [];
let chunk = [];
let l = 4;
array.forEach((el, i) => {
if (chunk.length < l) chunk.push(el);
if (chunk.length === l) {
r.push(chunk); chunk = [];
l = ( l === 4 ) ? 3 : 4;
}
})
console.log(r)
Yet another solution. Clearly everyone is having a good time with this one.
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
function divide(array, idx, num, result)
{
if(array.length <= idx) return result;
result.push(array.slice(idx,idx+num));
return divide(array, idx+num, num === 4 ? 3 : 4, result);
}
console.log(divide(array, 0, 4, []));
We can think of it as slicing elements from the array in a loop. It's just that we need to alternate between 4 and 3 instead of a constant value to slice.
We can parameterize alternating values by passing them in a function instead of hardcoding it in the solution like below:
Use Array##slice and
Just swap current and next like this by using destructuring assignment to achieve the solution.
Sub array sizes(4,3) can be modified outside actual logic or can be passed in a function to have flexible solution.
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
function TransformArray(array, current, next) {
let start = 0,
ans = [];
while (start < array.length - 1) {
ans.push(array.slice(start, start + current));
start += current;
[current, next] = [next, current]; //swap the count of array size
}
return ans;
}
console.log(TransformArray(array, 4, 3));
console.log(TransformArray(array, 3, 3));
Here's kind of a different way of doing this, I'm expanding a bit to allow you to arbitrarily pass array lengths, this allows the PM to change their mind any time and it isn't a big deal.
This could be cleaned up a bit, I wanted to leave it more verbose to make it easier to read.
// Setup the function getting in:
// an array
// first array's length
// second array's length
const arrayParser = (inArr,arr1len,arr2len) => {
// Create a new array.
let outArr = [];
// Basic forEach is basic, we need the value and the index.
inArr.forEach((val,idx) => {
// If the index's modulus of the total of the two array lengths is:
// 0 OR the first array's length
// Push a new empty array.
if (idx%(arr1len+arr2len)===0 || idx%(arr1len+arr2len)===arr1len) {
// Create a new array with the current value
outArr.push([]);
}
// Push the value to the last array in the out multidimensional array
outArr[outArr.length-1].push(val);
});
// You got it.. return the array.
return outArr;
};
// Single Dimensional Array
const singleArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30];
// Parse it.
// Expects:
// A Single Dimensional Array
// Length of the first array
// Length of the second array
console.log(arrayParser(singleArray,10,4));
console.log(arrayParser(singleArray,2,4));
console.log(arrayParser(singleArray,3,4));
console.log(arrayParser(singleArray,4,3));
console.log(arrayParser(singleArray,1,2));
This works, because you know the length of each of the inner arrays, so you don't really need to figure out anything.
Here's a 4,3 set broken out.
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
Push a new array at 0 and 4.
4+3
index total Modulus
0 % 7 = 0 <-- push [], push 1
1 % 7 = 1 <-- push 2
2 % 7 = 2 <-- push 3
3 % 7 = 3 <-- push 4
4 % 7 = 4 <-- push [], push 5
5 % 7 = 5 <-- push 6
6 % 7 = 6 <-- push 7
7 % 7 = 0 <-- push [], push 8
8 % 7 = 1 <-- push 9
9 % 7 = 2 <-- push 10
10 % 7 = 3 <-- push 11
11 % 7 = 4 <-- push [], push 12
12 % 7 = 5 <-- push 13
13 % 7 = 6 <-- push 14
Returns
[[1,2,3,4],[5,6,7],[8,9,10,11],[12,13,14]]
It ain't pretty and as much as I try to write functional code, it's pretty easy with a while loop...
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
const newArray = [];
let i = 0;
while (i < array.length) {
let four = array.slice(i, i + 4)
if (!(four.length > 0)) {
break;
}
newArray.push(four)
i += 4;
let three = array.slice(i, i + 3);
if (!(three.length > 0)){
break;
}
newArray.push(three);
i += 3;
}
return newArray