Merge Sorted Array leetcode - javascript

var merge = function(nums1, m, nums2, n) {
//contcating two array
let array = nums2.concat(nums1)
// sort the array
array.sort((a,b) => a-b)
// remove element > m+n length
return array.slice(m+n-n)
};
This ^ function is returning -> [1,2,3,0,0,0]
if i'm applying console then answer is same as expected -> [1,2,2,3,5,6]
Why is this happening ?

Remove slice function from the end of the function.
slice(m+n-n) slices your sorted array and returns array from index m+1 to the last index.
var merge = function(nums1, m, nums2, n) {
//contcating two array
let array = nums2.concat(nums1)
// sort the array
array.sort((a,b) => a-b)
// remove element > m+n length
return array.slice(m+n-n);
};
console.log(merge([2,4,8,9],4,[0,4,6,9],4));
You can use the following function to merge and then sort the two arrays.
Time complexity of this approach is O(nlogn)
function merge(arr1,arr2){
return [...arr1,...arr2].sort();
}
console.log(merge([4,8,6],[1,3,9,10]));
The second approach runs in O(n) time.
function merge(arr1,m,arr2,n){
let result = [];
let i=0 , j = 0 ;
while(i<m && j<n){
if(arr1[i]<arr2[j]){
result.push(arr1[i]);
i++;
}else{
result.push(arr2[j]);
j++;
}
}
while(i<m){
result.push(arr1[i]);
i++;
}
while(j<n){
result.push(arr2[j]);
j++;
}
return result;
}
console.log(merge([4,5,6],3,[1,3,8,10],4));

I did not see any problem with your code and it works, I just added another code. I think the problem with your console.log.
var merge = function(nums1, m, nums2, n) {
//contcating two array
let array = nums2.concat(nums1)
// sort the array
array.sort((a,b) => a-b)
// remove element > m+n length
return array.slice(m+n-n)
};
console.log(merge([1,2,3,0,0,0],3,[2,5,6],3));
var merge = function(nums1, m, nums2, n) {
return nums1.slice(0, m)
.concat(nums2.slice(0, n))
.sort((i, j) => i - j);
};
console.log( merge([1,2,3,0,0,0],3,[2,5,6],3))

Related

How to convert the index of a flatten multidimensional array (any depth) to the original index?

For example, suppose I have a multidimensional array:
const a1=[["a",["a","b"]],"b",[["b"],"c"]];
which the flatten from is
const a2=["a","a","b","b","c","c"];
and accessing a2[2] is equivalent to a1[0][1][1], also access a2[3] is equivalent to a1[1].
However I'm not interested in what the element is in such index, instead I want a function that can convert the index of a2 equivalent to in a1, eg:
magicFunction(2,[["a",["a","b"]],"b",[["b"],"c"]])
returns [0,1,1],
also
magicFunction(3,[["a",["a","b"]],"b",[["b"],"c"]])
returns [1]. And the input multidimensional array can be any depth (more than 3 layers). How do I write such magic function?
Note: I know that may be easy to do if I know the array is 2d and the sub-array have same number of elements:
const a1=[["a","a"],["b","b"],["c","c"]];
const magicFunction=function(target,a){
let count=0;
for(let i=0;i<a.length;i++){
for(let j=0;j<a[i].length;j++){
if(count==target){
return [i,j];
}
count++;
}
}
return [];
};
alert(magicFunction(3,a1));
However now I need the function to accept a with n-dimensional array which d is unknown before testing, which I don't know how many layers of for-loop should I write to handle it, also the length of each sub-array may not be the same.
Here's a recursive function that generates the path by traversing the array and counting values until it reaches the n'th value. The function returns either an array of the path (if we reach the n'th value) or the current count (if we don't).
const magicFunction = (index, array, count=0) => {
for (let idx = 0; idx < array.length; idx++) {
if (Array.isArray(array[idx])) {
let res = magicFunction(index, array[idx], count)
if (Array.isArray(res)) return [idx].concat(res)
else count = res
}
else {
if (count == index) return [idx]
count++
}
}
return count
}
for (i = 0; i < 6; i++) {
res = magicFunction(i, [["a",["b","c"]],"d",[["e"],"f"]])
console.log(`${i} : [${res}]`)
}
You can flatten an array into a list of [element path] pairs and fetch paths from that list:
function* flatWithPaths(a, parent=[]) {
for (let [i, e] of a.entries()) {
if (Array.isArray(e))
yield* flatWithPaths(e, parent.concat(i))
else
yield [e, parent.concat(i)]
}
}
//
const a = [["a",["b","c"]],"d",[["e","f"],[[["g","h"]]]]]
let n = 0
for (let [elem, path] of flatWithPaths(a)) {
console.log(n++, elem, path.join())
}

Concise way to return a new array of N elements filled with iterated values from another array? Vanilla JavaScript

I have a given array with an undetermined quantity of elements, the array can be numbers or strings, then I need to generate a new array of N elements made from the iterated elements of the first array
I already have a function to do it, but it only works if the original array are consecutive numbers, it doesn't work with strings. I have a gazillion of ideas on how to achieve it. I could just concatenate the array to a new one until its equal or greater than the required quantity of elements, and then set the new array length to the required quantity, but is there a more concise and elegant way to do it?
IDEA 01 codepen
function populateArray(qty) {
// Array to populate from
let array = [1,2,3];
//Determine the Range Length of the array and assign it to a variable
let min = array[0];
let max = array[array.length - 1];
const rangeLength = (max - min + 1);
//Initialize uniqueArray which will contain the concatenated array
let uniqueArray = [];
//Test if quantity is greater than the range length and if it is,
//concatenate the array to itself until the new array has equal number of elements or greater
if (qty > rangeLength) {
//Create an array from the expansion of the range
let rangeExpanded = Array.from(new Array(rangeLength), (x,i) => i + min);
while (uniqueArray.length < qty) {
uniqueArray = uniqueArray.concat(rangeExpanded);
}
}
// Remove additional elements
uniqueArray.length = qty
return uniqueArray;
}
console.log(populateArray(13))
IDEA 02 codepen, but it fills the new array 13 times with the whole original array, not iterated items
// FILL A NEW ARRAY WITH N ELEMENTS FROM ANOTHER ARRAY
let array = [1,2,3];
let length = 13;
let result = Array.from( { length }, () => array );
console.log(result);
the expected result is [1,2,3,1,2,3,1,2,3,1,2,3,1] if the original array were made of strings the expected result would be [dog,cat,sheep,dog,cat,sheep,dog,cat,sheep,dog,cat,sheep,dog]
You can tweak your second idea a bit - calculate the number of times you need to repeat the initial array to come up with the required number of total items, then flatten it and .slice:
let array = [1,2,3];
let length = 13;
const fromLength = Math.ceil(length / array.length);
let result = Array.from( { length: fromLength }, () => array )
.flat()
.slice(0, length);
console.log(result);
I'll go with #CertainPerformance's answer. But here's a different approach, just for thinking-out-of-the-box purposes
// A function for getting an index up to length's size
function getIDX(idx, length){
return idx <= length ? idx : getIDX(idx-length, length);
}
const newArrayLength = 13;
const sourceArray = [1,2,3];
const resultArray = [];
for(let i=0; i< newArrayLength; i++){
resultArray[i]=sourceArray[getIDX(i+1, sourceArray.length)-1];
}
EDIT 1:
I was comparing the performance of this approach versus the others here described and it seems that if you wanted to create a very large new array (ex: newArrayLength= 10000) the getIDX() function takes a lot to finish because of the size of the call stack. So I've improved the getIDX() function by removing the recursion and now the complexity is O(1) check it out:
function getIDX(idx, length){
if (length === 1) {return idx};
const magicNumber = length * (Math.ceil(idx/length)-1);
return idx - magicNumber;
}
With the new getIDX() function this approach seems to be the most performant.
You can take a look to the tests here:
https://jsbench.me/v7k4sjrsuw/1
You can use a generator function that will create a repeating sequence from an input. You can add a limit to the generated sequence and simply turn it into an array:
function* repeatingSequence(arr, limit) {
for(let i = 0; i < limit; i++) {
const index = i % arr.length;
yield arr[index];
}
}
const generator = repeatingSequence(["dog", "cat", "sheep"], 10);
const result = Array.from(generator);
console.log(result);
Alternatively, you can make an infinite repeating sequence with no limit and then generate as many elements as you want for an array:
function* repeatingSequence(arr) {
let i = 0
while(true) {
const index = i % arr.length;
yield arr[index];
i++;
}
}
const generator = repeatingSequence(["dog", "cat", "sheep"]);
const result = Array.from({length: 10}, () => generator.next().value);
console.log(result);
You can use modulo operator. Special thanks to #Vlaz for shorten version:
Array.from({ length:length }, (e, i) => array[ i % array.length ])
An example:
let array = [1,2,3];
let length = 13;
const result = Array.from({ length:length },
(e, i) => array[ i % array.length ]);
console.log(result);

How to remove certain elements from an array into a new array and leave the others only the original array?

How to write a function to remove certain elements into a new array and leave the original array with only the remaining elements?
the first part is easy using a for loop pushing the even numbers into a new array but mutating the original array to leave only the odd numbers is hard
function remove(arr, cb){
var removed = [];
var newArr = [];
for(var i = 0; i < arr.length; i++) {
if(cb(arr[i], i, arr)) {
removed.push(arr[i]);
}
}
return removed;
}
Use an else statement to fill newArr with values that should stay in the original arr, then empty it using splice() before copying the items from newArr back into it.
function remove (arr, cb) {
var removed = [];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (cb(arr[i], i, arr)) {
removed.push(arr[i]);
} else {
newArr.push(arr[i]);
}
}
arr.splice(0);
for (var i = 0; i < newArr.length; i++) {
arr.push(newArr[i]);
}
return removed;
}
Welcome to Stackoverflow!
Personally, I'd avoid anything that mutates an input parameter, as this increases code complexity and makes it hard to reason about what's happening from the calling side.
Instead, I'd write a method that returns an array of two arrays. This can be easily split into two variables at the calling end using by using array destructuring.
See the example below:
const splitArr = (arr, pred) =>
arr.reduce(
(prev, curr, idx) => {
prev[+pred(curr, idx, arr)].push(curr);
return prev;
}, [[], []]
);
// usage //
const myArr = [1, 2, 3, 4];
const [arr1, arr2] = splitArr(myArr, x => x > 2);
console.log(arr1);
console.log(arr2);
Because pred is a function that returns a boolean value, we can co-erce this value to 0 or 1 using +someBoolean. We can then use this value as an index to decide into which of the two output arrays the value should be pushed.
You were definitely on the right track with your solution, a couple tweaks and we can make it very readable and also very easy to work with. I tried to keep the format of what it looked like you were doing.
I do take advantage of destructuring here, this could be returned as just an object, and then reference the properties.
const myArr = [0,1,2,3,4,5,6,7,8,9,10];
const splitItems = (arr, logicFunc) => {
let secondSet = []
const firstSet = arr.filter(v => {
if (logicFunc(v)) return true
else secondSet.push(v)
})
return { firstSet, secondSet }
}
const myLogicFunc = v => (v < 3 || v == 9)
const { firstSet, secondSet } = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${firstSet}`) // My first set: 0,1,2,9
console.log(`My second set: ${secondSet}`) // My second set: 3,4,5,6,7,8,10
/* OR without destructuring:
const myArrays = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${myArrays.firstSet}`)
console.log(`My second set: ${myArrays.secondSet}`)
*/
Please let me know if you have any questions
In modern JavaScript apps we do not mutate arrays we create new array, this avoids side effects, so what we do is create two new arrays
const split = (source, conditionFunc) = [ source.filter(i => conditionFunc(i)), source.filter(i => !conditionFunc(i))];
Then you have an array of two arrays of the values that meed condition and those that don't and you have not caused any side effects.
const odssAndEvens = split(source, i => i % 2 === 1);
Or with reduce so you don't iterate the array twice
const split = (source, conditionFunc) = source.reduce((results, item) => {
if (conditionFunc(item)) {
results[0].push(item);
} else {
results[1].push(item);
}
return results;
}, [[],[]]);

Sum multidimensional array elements if they have an duplicate

I have an array like this:
array = [[3, 'name'],[4, 'lastname'],[2, 'name'],[4, 'lastname']]
I need to sum the values if they have equal second element:
array = [[5, 'name'],[8, 'lastname']]
I tried to sort array by alphabetic order, I know there won't be more than one duplicate, and then if item is equal to next one sum their values, else do nothing. Maybe the condition should be stringified, this is just to show what I tried...
for(var n=0; n<finish.length; n++) {
if(finish[n][1] === finish[n+1][1]) {
finish[n][0] += finish[n+1][0];
} else {
finish[n][0] = finish[n][0];
}
}
The loop returns this error: Cannot read property '1' of undefined
You can use reduce to summarise the data into an object. Use Object.values to convert the object into array
let array = [[3, 'name'],[4, 'lastname'],[2, 'name'],[4, 'lastname']];
let result = Object.values(array.reduce((c, v) => {
if (c[v[1]]) c[v[1]][0] += v[0]; //Add the first element if already exist
else c[v[1]] = v; //Assign the value if does not exist
return c;
}, {}));
console.log(result);
You can also use Map to get the desired output:
let array = [[3, 'name'],[4, 'lastname'],[2, 'name'],[4, 'lastname']];
let map = ((m, a) => (array.forEach(c => {
let [num, str] = c;
m.set(str, (m.get(str) || 0) + num);
}), m))(new Map(), array);
let result = [...map.entries()].map(([a, b]) => [b, a]);
console.log(result);
You go up to n< length, but you reference an index of n+1 in
if(finish[n][1] === finish[n+1][1])
So, in this case, once you get to n = 3, the n+1 part is referencing an element that is not defined, and thus the error.
To fix this, you need to go up to (n < length-1)

Function to create array of arrays in javascript

Given an array with characters such as ["A","P","P","L","E","S","A","R","E"], I'm trying to create a function that will loop over the elements in the array, and create an array for each character, which will then be put into a master array. The master array will end up looking like[["A"],["P","P"],["L"],["E"],["S"],["A"],["R"],["E"] at the end, using a comparator function to check values (a,b) => a == b. Essentially, it needs to check each successive letter, and if they are the same, group into their own array within the master. The two A's should not be grouped together, since they aren't successive.
var arr = ["A","P","P","L","E"];
var master = [];
arr.sort(function(a,b){
for(var i = 0; i <= arr.length; i++){
compare each element to its successor. if successor is the same, create array of like elements ["A"],["C","C"],["B"] within final array
if(i + 1 == i){
master.push(i);
}
}
});
Just loop through the array and compare the last value to the current one.
DO NOT SORT -- that will change the order of your input array!
const coolFn = (arr) => {
return arr.reduce((rez, value, index) => {
if (index !== 0 && rez[rez.length - 1][0] === value) {
rez[rez.length - 1].push(value);
} else {
rez.push([value]);
}
return rez;
}, []);
}
const rez = coolFn('APPLES ARE NOT A BANANA PUDDING CUP'.split(''));
console.log(rez);
Can be accomplished pretty easily with Set to get unique items, reduce to turn transform and filter to find matching elements:
const arr = ["A","P","P","L","E"]
// get unique keys by expanding to a Set
const letters = [...new Set(arr)].reduce((p, c) => {
// add all matching elements from original array to aggregate
p.push(arr.filter(i => i === c))
return p;
}, []);
console.log(letters);
edit: sorry, I missed the requirement (hidden in a comment in your code) that you only add by comparing each element to its successor. My solution creates an array of each letter with its number of occurrences
You might do as follows;
var arr = ["A","P","P","L","E"],
result = arr.reduce((p,c) => {var fi = p.findIndex(a => a[0] === c);
fi === -1 ? p.push([c]) : p[fi].push(c);
return p;
},[]);
console.log(result);
As per the grouping only the sequential duplicates the following should do;
var arr = ["A","P","P","L","E","S","A","R","E"],
stickTheSame = a => a.reduce((p,c) => (p[p.length-1][0] === void 0 ||
p[p.length-1][0] === c ? p[p.length-1].push(c)
: p.push([c]),
p),[[]]);
console.log(JSON.stringify(stickTheSame(arr)));

Categories

Resources