Related
I have to find a unique number in unsorted array, but my function returns wrong number, I can't understand why.
Here is my code:
function findUniq(arr) {
let sorted = [...arr].sort();
if (sorted.length === 0) return 0;
// do magic
let num = 0;
for (let i = 1; i < sorted.length; i++) {
if (sorted[num] !== sorted[i]) {
num++;
sorted[num] = sorted[i];
}
}
return num + 1;
}
const testArray = [9, 7, 7, 9, 6, 6, 5, 5, 5];
console.log(findUniq(testArray));
if I invoke findUniq([9,7,7,6,6,5,5,5]) it gives 4. What do I do wrong? Thanks in advance. I forgot to mention I have to have just one for loop to implement O(n) time complexity
I don't know why your solution doesn't work, but here is working code:
const arr = [9,9,7,7,8,6,1,6,5,5,5]
function findUnique(arr){
const sorted = [...arr].sort()
if(sorted[0] !== sorted[1]) return sorted[0]
if(sorted[sorted.length - 1] !== sorted[sorted.length - 2]) return sorted[sorted.length - 1]
for(let i = 0; i < sorted.length; i++){
if(sorted[i] !== sorted[i - 1] && sorted[i] !== sorted[i + 1]) return sorted[i]
}
return "no unique"
}
console.log(findUnique(arr))
This should work:
It returns only one unique number in array or undefined
If you want all unique numbers replace return arr[0] with return arr. It will then return array of all unique numbers (or empty array if there are not any)
function findUniq(arr) {
arr.filter((item, index) => {
arr.splice(index, 1)
const unique = !arr.includes(item)
arr.splice(index, 0, item)
return unique
})
return arr[0]
}
ES6 aproach:
function findUniq(arr) {
return arr
.map((c) => arr.filter((b) => c == b))
.filter((e) => e.length < 2)
.reduce((total, cur) => total.concat(cur), [])
}
You can use reduce and can find the item that repeats once in the last index.
var arr = [9,7,7,6,6,5,5,5]
var uniqueNumber;
arr.reduce((obj,val,index) =>
{
obj[val] ? ++obj[val] : obj[val] = 1;
if(index == (arr.length - 1))
{
uniqueNumber = Object.keys(obj).find(key => obj[key] === 1)
}
return obj
},{})
console.log(uniqueNumber)
To do it in a single O(n) loop, reduce, keeping track of counts as well as a set of singular items.
function findUniq(arr) {
let [single] = arr.reduce((acc, el) => {
if (!acc[el]) acc[el] = 0;
if (++acc[el] === 1) acc.singular.add(el);
else acc.singular.delete(el);
return acc;
}, { singular: new Set() }).singular;
return single;
}
const input = [2, 2, 9, 7, 7, 6, 6, 5, 5, 5];
const result = findUniq(input);
console.log(result);
Is there a way I can make my code even more simple?
FYI, I'm a beginner and I've been learning JavaScript only for a week.
Thank you!
let array = [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20];
// from above to [[1, 1, 1, 1], [2, 2, 2], 4, 5, 10, [20, 20], 391, 392, 591]
const answer = (arr) => {
arr.sort((a, b) => {
return a - b;
});
let counter = 0;
arr.forEach((num, i) => {
if (arr[i] === arr[i+1]) {
counter++;
} else if (arr[i] !== arr[i + 1] && arr[i] === arr[i-1]) {
arr[i-counter] = arr.slice(i-counter, i + 1);
counter = 0;
}
});
arr.forEach((num, i) => {
while (arr[i][0] && arr[i][0] === arr[i + 1]) {
arr.splice(i + 1, 1);
}
});
return arr;
}
const newArray = answer(array);
console.log(newArray);
Another option. First push each number in object's appropriate array, then convert object to resulting array as needed.
let arr = [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20];
const obj = {}, res = [];
for(e of arr) !obj[e] ? obj[e] = [e] : obj[e].push(e);
for(a in obj) obj[a].length === 1 ? res.push(obj[a][0]) : res.push(obj[a]);
console.log(res);
You can make it simpler just first sort and then group them.
let array = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20],
last = null;
const result = array
.sort((a, b) => a - b)
.reduce((acc, curr, index, arr) => {
// Getting the next element in array
const next = arr[index + 1];
if (last && last === curr) acc[acc.length - 1].push(curr);
else if (next && next === curr) acc.push([curr]);
else acc.push(curr);
// Changing the last to the curr
last = curr;
return acc;
}, []);
console.log(result);
Approach that uses a Map to group the elements then sorts the Map entries by key so as not to mutate original array at all
let array = [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20];
const answer = (arr) =>{
const m = new Map();
arr.forEach(n => m.set(n, (m.get(n) || []).concat(n)));
return [...m.entries()].sort(([a],[b])=> a-b)
.map(([k,v]) => v.length > 1 ? v : v[0])
}
console.log(answer(array))
I found some solutions to my question in other languages. When I converted them to javascript, it would not create an array.
const find_digits = (n, sum, out, index) => {
if (index > n || sum < 0) return;
let f = "";
if (index == n) {
if (sum == 0) console.log(out); // Success!
return;
}
for (var i = 0; i < 10; i++) {
out[index] = i;
find_digits(n, sum - i, out, index + 1);
}
}
const subset_sum = (n, sum) => {
var out = [].fill(false, 0, n + 1);
for (var i = 0; i < 10; i++) {
out[0] = i;
find_digits(n, sum - i, out, 1);
}
return out;
}
console.log(subset_sum(3, 17)); // Output: [9,9,9]
The first log is successful, but the final log returns [9,9,9]
I would appreciate some help.
I might make the recursion a bit more explicit. To my mind there are a number of different base-cases, for various low values of n and s:
if s < 0, then there are no results
if s == 0, then the only result is a string of n zeroes
if n == 1, then
if s < 10, then the only result is the digit s
otherwise, there are no results
The recursive case involves taking each digit as a potential first digit, then joining it with each of the results involved in recursing, taking that amount from the total and using a digit count one smaller.
Here's an implementation of that:
const subsetSum = (n, s) =>
s < 0
? []
: s == 0
? ['0' .repeat (n)]
: n == 1
? s < 10 ? [String(s)] : []
: // else
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .flatMap (
k => subsetSum (n - 1, s - k) .map (p => k + p)
)
console .log (
subsetSum (3, 17)
) //~> ["089", "098", "179", "188", "197", "269", "278", ..., "971", "980"] (63 entries)
.as-console-wrapper {min-height: 100% !important; top: 0}
Given your comment about licenses, I assume that it's really strings of digits you want and not numbers. That's what this returns. If you want numbers, you will need to remove all those that start with 0 and convert the strings to numbers. (Thus, 89 would not be included in subset(17, 3) even thought "089" is a legitimate digit string, because 89 is only a two-digit number.)
Update
I just realized that the s == 0 case can be subsumed in the recursive one. So it's actually a bit simpler:
const subsetSum = (n, s) =>
s < 0
? []
: n == 1
? s < 10 ? [String(s)] : []
: // else
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .flatMap (
k => subsetSum (n - 1, s - k) .map (p => k + p)
)
Or, phrased slightly differently, as
const subsetSum = (n, s) =>
s < 0 || (n <= 1 && s >= 10)
? []
: n == 1
? [String(s)]
: // else
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .flatMap (
k => subsetSum (n - 1, s - k) .map (p => k + p)
)
const find_digits = (n, sum, out, index) => {
if (index >= n) return;
out[index] = 9;
find_digits(n, sum, out, index +1);
}
const subset_sum = (n, sum) => {
var out = [].fill(false, 0, n + 1);
find_digits(n, sum, out, 0);
return out;
}
console.log(subset_sum(3, 17));
I guess the problem is with the check at the beginning of the find_digit() function for the number of times you can call this function won't exceed by any means 3 times as per your arguments passed in the subset_sum() function. I have redacted the code to the bare minimum and produced the same result as yours. If you can give me clarification as to the purpose of this code. I would be glad to help.
You should call your function without console, like this:
subset_sum(3, 17)
Because you write to console inside your code.
Your trouble is that your subset_sum returning at the end array out.
So, you have 2 options:
- call your function by name
- returning at the end of subset_sum just "return"
The function below allows you to pass in a different base/alphabet.
It first generates the permutations, then filters the results based on the value you passed in as the second parameter.
const BaseSystem = {
bin : '01',
dec : '0123456789',
hex : '0123456789ABCDEF'
};
Object.keys(BaseSystem).forEach(k =>
Object.assign(BaseSystem, { [k] : BaseSystem[k].split('') }))
const main = () => {
console.log(subsetSum(4, v => v === 2, BaseSystem.bin))
console.log(subsetSum(2, 4)) // Default = BaseSystem.dec
console.log(subsetSum(3, 0xF, BaseSystem.hex))
}
const subsetSum = (size, sum, alpha=BaseSystem.dec) => {
if (typeof sum === 'string') sum = parseInt(sum, alpha.length)
return getPermutations(alpha, size)
.filter(v => ((result) =>
typeof sum === 'function' ? sum(result, v) : sum === result
)(parseReduce(alpha.length, ...v))).map(v => v.join(''))
}
const parseReduce = (base, ...v) =>
v.reduce((t, x) => t + parseInt(x, base), 0)
/** Adapted From: https://stackoverflow.com/a/59028925/1762224 */
const getPermutations = (list, len) => {
const base = list.length
const counter = Array(len).fill(base === 1 ? arr[0] : 0)
if (base === 1) return [counter]
const results = []
const increment = i => {
if (counter[i] === base - 1) {
counter[i] = 0
increment(i - 1)
} else counter[i]++
}
for (let i = base ** len; i--;) {
const subResults = []
for (let j = 0; j < counter.length; j++)
subResults.push(list[counter[j]])
results.push(subResults)
increment(counter.length - 1)
}
return results
}
main();
.as-console-wrapper {min-height: 100% !important; top: 0}
I'm looking at this leetcode question, and am having an issue completing the naive approach. I was able to come to an optimal solution here. But I'm not sure what's wrong with my naive attempt.
The question is as follows:
Given two integer arrays A and B, return the maximum length of an
subarray that appears in both arrays.
Example:
Input: A: [1,2,3,2,1] B: [3,2,1,4,7]
Output: 3
Explanation: The repeated subarray with maximum length is [3, 2, 1].
Here is my current code:
var findLength = function(a, b) {
if (a.length === 0 || b.length === 0) {
return 0;
}
let aWithoutFinalNumber = a.slice(0, a.length - 1);
let bWithoutFinalNumber = b.slice(0, b.length - 1);
let aFinalNumber = a[a.length - 1];
let bFinalNumber = b[b.length - 1];
// matching numbers
if(aFinalNumber === bFinalNumber) {
return 1 + findLength(aWithoutFinalNumber, bWithoutFinalNumber);
} else { // mismatch. Compete to find the maximum length.
return Math.max(findLength(a, bWithoutFinalNumber), findLength(aWithoutFinalNumber, b));
}
};
My solution passes several test cases, but fails on cases like a: [0,1,1,1,1] b: [1,0,1,0,1]. Any insight on my mistake would be appreciated!
The problem comes from the way you calculate the max length when the last elements match. here is a minimal example:
var findLength = function(a, b) {
if (a.length === 0 || b.length === 0) {
return 0;
}
let aWithoutFinalNumber = a.slice(0, a.length - 1);
let bWithoutFinalNumber = b.slice(0, b.length - 1);
let aFinalNumber = a[a.length - 1];
let bFinalNumber = b[b.length - 1];
// matching numbers
if(aFinalNumber === bFinalNumber) {
return 1 + findLength(aWithoutFinalNumber, bWithoutFinalNumber); //< -- problem here
} else { // mismatch. Compete to find the maximum length.
return Math.max(findLength(a, bWithoutFinalNumber), findLength(aWithoutFinalNumber, b));
}
};
console.log(findLength([1, 0, 2, 1], [1, 0, 3, 1]));
If there is any match you add 1 to the max length but that's not necessarily true if there is a mismatch later. Here is a shortened version what happens with illustration for easier understanding:
[1, 0, 2, 1]
^-------|
[1, 0, 3, 1] | -- match, max length +1
^-------|
______
[1, 0, 2, 1]
^----------|
[1, 0, 3, 1] | -- mismatch, max length +0
^----------|
______
[1, 0, 2, 1]
^-------------|
[1, 0, 3, 1] | -- match, max length +1
^-------------|
______
[1, 0, 2, 1]
^----------------|
[1, 0, 3, 1] | -- match, max length +1
^----------------|
When you total all the matches, you get 3 however, the count should have been reset when there was a mismatch.
One simple change that can be done to the algorithm to avoid this problem is to pass the current count as an argument to the function. This way, you can control when the count needs to be reset:
var findLength = function(a, b, maxSoFar = 0) { //<-- default count of zero
if (a.length === 0 || b.length === 0) {
return maxSoFar; //<-- return the count
}
let aWithoutFinalNumber = a.slice(0, a.length - 1);
let bWithoutFinalNumber = b.slice(0, b.length - 1);
let aFinalNumber = a[a.length - 1];
let bFinalNumber = b[b.length - 1];
// matching numbers
if(aFinalNumber === bFinalNumber) {
const newMax = maxSoFar + 1; //<-- increment the count
return Math.max(newMax, findLength(aWithoutFinalNumber, bWithoutFinalNumber, newMax)); //<-- return the newMax in case the next is a mismatch
} else { // mismatch. Compete to find the maximum length.
return Math.max(findLength(a, bWithoutFinalNumber), findLength(aWithoutFinalNumber, b)); //<-- reset the count
}
};
console.log(findLength([1, 0, 2, 1], [1, 0, 3, 1]));
console.log(findLength([1, 2, 3, 2, 1], [3, 2, 1, 4, 7]));
console.log(findLength([0, 1, 1, 1, 1], [1, 0, 1, 0, 1]));
You could use nested loop and when same element occur in both arrays you could start incrementing both of those indexes until elements in both arrays are the same. The result that gives you the largest set of elements will be the returned result.
function maxSub(a, b) {
let result = null;
function findAll(i, j) {
const res = [];
if (a[i] !== b[j] || a[i] == undefined || b[j] == undefined) {
return res;
}
return res.concat(a[i], ...findAll(i + 1, j + 1))
}
a.forEach((e, i) => {
b.forEach((c, j) => {
if (e == c) {
const sub = findAll(i, j);
if (!result || sub.length > result.length) {
result = sub
}
}
})
})
return result;
}
console.log(maxSub([0, 1, 1, 1, 1], [1, 0, 1, 0, 1]))
console.log(maxSub([1, 2, 3, 2, 1], [3, 2, 1, 4, 7]))
you can use dp with a table as well. i tried the other code, and it gave errors in cases like: [0,0,0,0,1,0,0] and [0,0,0,0,0,1,0].
Here is a python code for the same.
def findLength(a, b):
if len(a)==0 or len(b)==0:
return 0
n=len(a)
m=len(b)
dp=[[0 for _ in range(n+1)] for _ in range(m+1)]
maxSoFar=0
for i in range(1,m+1):
for j in range(1,n+1):
if a[j-1]==b[i-1]:
dp[i][j]=dp[i-1][j-1]+1
maxSoFar=max(maxSoFar,dp[i][j])
else:
dp[i][j]=0
return maxSoFar
I want to sort only odd numbers without moving even numbers. For example, when I write :
sortArray([5, 3, 2, 8, 1, 4])
The expected result is :
[1, 3, 2, 8, 5, 4]
I am new to JavaScript and I came across a challenge on the Internet that has me perplexed. I normally wouldn't post asking for a solution on the Internet, BUT I have tried for hours and I would like to learn this concept in JavaScript.
The challenge states :
You have an array of numbers.
Your task is to sort ascending odd numbers but even numbers must be on their places.
Zero isn't an odd number and you don't need to move it. If you have an empty array, you need to return it.
Here is my code so far, please take it easy on me I am in the beginning stages of programming.
function sortArray(array) {
let oddNums = [];
for(let i = 0; i < array.length; i++) {
if(array[i] % 2 !== 0) {
oddNums.push(array[i]);
}
}
oddNums = oddNums.sort((a,b)=> a-b);
array.concat(oddNums);
array = array.sort((a,b) => a-b);
return array;
}
You could take a helper array for the odd indices and another for the odd numbers, sort them and apply them back on the previously stored indices of the original array.
var array = [5, 3, 2, 8, 1, 4],
indices = [];
array
.filter((v, i) => v % 2 && indices.push(i))
.sort((a, b) => a - b)
.forEach((v, i) => array[indices[i]] = v);
console.log(array);
Here's a solution using mostly the built-in array methods. Get a list of just the odds, sort it, then map through the original, replacing each item with the first sorted odd if the item is odd, or itself if even:
const array = [5, 3, 2, 8, 1, 4] // to: [1, 3, 2, 8, 5, 4]
function sortOddsOnly(arr) {
const odds = arr
.filter(x => x%2)
.sort((a, b) => a - b);
return arr
.map(x => x%2 ? odds.shift() : x);
}
console.log(sortOddsOnly(array));
I have a solution like this.
Build a sorted odd number array 1st, and then fill the rest of even numbers in order:
const arr = [5, 3, 2, 8, 1, 4];
const odd = arr.filter(i => i%2 !== 0).sort();
let i = 0,
result = [];
arr.forEach(e => {
if (e%2 === 0) {
result.push(e)
} else {
result.push(odd[i]);
i++;
}
});
console.log(result);
just do:
arr.sort((a, b) => a%2 && b%2 ? a - b : 0)
If that works depends on the sort algorithm your browser uses.
A browserindependent version:
for(const [i1, v1] of arr.entries())
for(const [i2, v2] of arr.entries())
if( v1%2 && v2%2 && (i1 < i2) === (v1 > v2))
([arr[i1], arr[i2]] = [v2, v1]);
One of the possible solutions is this. What I have done is created new array odd(array with odd position in original array using Array.prototype.filter) and then sort that array using Array.prototype.sort. Then using Array.prototype.map change value of all odd element of original array with odd array.
x1=[5, 3, 2, 8, 1, 4];
function sortArray(array) {
var odd = array.filter((x,i) => (i+1) % 2 ).sort((a,b) => a > b); //sort odd position and store that in new array
return array.map((x,i) => (i+1) % 2 ? odd.shift() : x ); //if i is odd then replace it with element from
//odd array otherwise keep the element as it is
}
console.log(sortArray(x1));
Here is a possible solution using a slightly customized selection sort :
var xs = [5, 3, 2, 8, 1, 4];
console.log(sortOddsOnly(xs));
function sortOddsOnly (xs) {
var n = xs.length;
for (var i = 0; i < n - 1; i++) {
if (xs[i] % 2 === 1) {
for (var j = i + 1; j < n; j++) {
if (xs[j] % 2 === 1) {
if (xs[i] > xs[j]) {
var min = xs[j];
xs[j] = xs[i];
xs[i] = min;
}
}
}
}
}
return xs;
}
The first two if guarantee that we swap only odd numbers (x % 2 === 1 means "x is odd").
def sort_array(source_array):
b = sorted([n for n in source_array if n % 2 != 0])
c = -1
d = []
for i in source_array:
c = c+1
if i % 2 != 0 :
d.append(c)
for x in range (len(d)):
z = d[x]
source_array[z] = b[x]
return source_array