I'm trying this quiz on Codewars: Smallest unused ID
You've got much data to manage and of course you use zero-based and non-negative ID's to make each data item unique!
Therefore you need a method, which returns the smallest unused ID for your next new data item...
Note: The given array of used IDs may be unsorted. For test reasons there may be duplicate IDs, but you don't have to find or remove them!
In short, You are given an array. What the quiz requires is to find the smallest unused number.
For example:
[0,1,2,3,5] // Output: 4
[1,2,3,5] // Output: 0
[0,1,2,3,4,5] // Output: 6
[0,0,0,0,0,0] // Output: 1
[0,1,0,2,0,3] // Output: 4
My code can't pass some tests. Inputs for the tests are hidden, so I am unable to figure out the causes. What is causing the issue?
const nextId = ids => {
let lowestId;
if (Math.max(...ids) + 1 === ids.length) {
lowestId = Math.max(...ids) + 1;
} else {
let sortedIds = ids.sort((a, b) => a - b);
for (let i = 0; i < sortedIds.length; i++) {
if (i !== sortedIds[i]) {
lowestId = i;
break;
}
}
}
return lowestId
}
console.log(nextId([0, 1, 2, 3, 5])) // 4
console.log(nextId([1, 2, 3, 5])) // 0
console.log(nextId([0, 1, 2, 3, 4, 5])) // 6
console.log(nextId([0, 0, 0, 0, 0, 0])) // 1
console.log(nextId([0, 1, 0, 2, 0, 3])) // 4
You're over complicating it
function doStuff(stuff)
{
for(let i = 0; i < stuff.length + 1; i++)
{
if(stuff.indexOf(i) == -1)
return i;
}
}
It looks like you missed this line of the instructions:
"Note: The given array of used IDs may be unsorted. For test reasons there may be duplicate IDs, but you don't have to find or remove them!"
With the logic of your code, if there's a duplicate then the value won't match the index. You need to actually compare the values to account for duplicates.
function nextId(ids) {
let lowestId = 0;
let sortedIds = ids.sort((a, b) => a - b);
for (let i = 0; i < sortedIds.length; i++) {
if (lowestId === sortedIds[i] && lowestId !== sortedIds[i + 1]) {
lowestId++;
}
else if (lowestId !== sortedIds[i + 1]) {
return lowestId;
}
}
return lowestId
}
This approach takes an object for seen values.
The start node is -1, because the first missing id could be zero.
Performance:
To collect all nodes: O(n).
To find the missing smallest id: smaller than O(n).
function nextId(ids){
const seen = {};
let id = -1;
for (const id of ids) seen[id] = true;
while (seen[++id]) ;
return id;
}
console.log(nextId([0, 1, 2, 3, 5])); // 4
console.log(nextId([1, 2, 3, 5])); // 0
console.log(nextId([0, 1, 2, 3, 4, 5])); // 6
console.log(nextId([0, 0, 0, 0, 0, 0])); // 1
console.log(nextId([0, 1, 0, 2, 0, 3])); // 4
Related
I wrote a solution that allows me to get an array of indexes from the first array which is the intersection of indexes from two sorted arrays and I'd like to know why this solution is wrong. When I check it I get the correct array of indexes from the first array but the interviewer told me that this is wrong.
Thanks a lot for the help and explanations. I have no commercial experience yet. Sorry for some mistakes in English, as I am from Ukraine and I improve this language.
// first example of input:
// const arr1 = [1, 2, 2, 2];
// const arr2 = [1, 1, 2, 2];
// second example of input:
const arr1 = [1, 2, 2, 3, 4, 5, 6, 7, 9, 9, 20];
const arr2 = [1, 2, 3, 3, 5, 8, 9, 9, 21];
// first example of output:
// - [0, 1, 2]
// - [0, 1, 3]
// - [0, 2, 3]
// second example of output:
// - [0, 1, 3, 5, 8, 9]
// - [0, 2, 3, 5, 8, 9]
//function compareItemsFn, length1, length2 - from conditions to this task
const compareItemsFn = (index1, index2) => {
switch (true) {
case arr1[index1] === arr2[index2]: return 0;
case arr1[index1] < arr2[index2]: return -1;
case arr1[index1] > arr2[index2]: return 1;
default: return undefined;
}
};
const length1 = arr1.length;
const length2 = arr2.length;
// function intersectionIndexes - my solution
function intersectionIndexes(compareItemsFn, length1, length2) {
let indexesIntersectionArray = [];
let i = 0;
let j = 0;
while (i < length1 && j < length2) {
if (compareItemsFn (i, j) === 0) {
indexesIntersectionArray.push(i);
i++;
j++;
} else if (compareItemsFn (i, j) === 1) {
j++;
} else {
i++;
}
}
return indexesIntersectionArray;
};
const result = intersectionIndexes(compareItemsFn, length1, length2);
If you are certain that your solution works then perhaps it was not wrong in the sense that it gave the wrong answer but rather in the way you solved the problem.
The following code is a simplification of your solution. It takes the two arrays as parameters instead of the value of their length property so the solution isn't tied to the global variables arr1 and arr2. You should always favor implementing solutions that are generalised.
In place of your compareItemsFn function, the Math.sign() method from the standard library is used. Some times in interview situations you can be asked to implement functionality which can be found in the standard library and what the interviewer is looking to see is if you are aware of it.
function simplified(arrayOne, arrayTwo) {
let result = [];
let indexOne = 0;
let indexTwo = 0;
while (indexOne < arrayOne.length && indexTwo < arrayTwo.length) {
let signCheck = Math.sign(arrayOne[indexOne] - arrayTwo[indexTwo]);
if (signCheck == 0) {
result.push(indexOne);
indexOne++;
indexTwo++;
}
else if ( signCheck > 0) {
indexTwo++;
}
else {
indexOne++;
}
}
return result;
}
I want to write a function with for-loops that finds the index of the number 1 in an array and returns the difference to the index of the number 2 that is closest to number 1 (number 1 only appears once). For instance:
Input: [1, 0, 0, 0, 2, 2, 2]
Output: 4
Input: [2, 0, 0, 0, 2, 2, 1, 0, 0 ,2]
Output: 1
My try
function closest (array) {
let elem=array.findIndex(index=>index === 1)
let numberplus=0;
let numberminus=0;
for (let i=elem; i<array.length; i++){
if (array[elem+1] === 2)
{numberplus+=array[elem+1]-elem;}
break;
}
for (let i=elem; i>=0; i--) {
if (array[elem-1] ===2)
{numberminus+=array[elem-1]-elem;}
break;
}
if (numberplus < numberminus) {
return numberplus
} else {
return numberminus}
}
When invoked, the function just returns '0'. Thanks for reading!
Take the position of 1 as starting point and loop up and (if necessary) down the array:
const log = (arr, d) => console.log(`mimimal distance [${arr.join()}]: ${d}`);
const arr = [2, 0, 0, 0, 2, 2, 1, 0, 0, 2];
const arr2 = [1, 0, 0, 0, 2, 2, 2];
const arr3 = [2, 0, 1, 0, 2, 2, 2];
const arr4 = [2, 1, 0, 0, 2, 2, 2];
log(arr, clostes(arr));
log(arr2, clostes(arr2));
log(arr3, clostes(arr3));
log(arr4, clostes(arr4));
function clostes(arr) {
// determine position of 1
const indxOf1 = arr.indexOf(1);
// create array of distances
const distances = [0, 0];
// forward search
for (let i = indxOf1; i < arr.length; i += 1) {
if (arr[i] === 2) {
break;
}
distances[0] += arr[i] !== 2 ? 1 : 0;
}
// if 1 is # position 0 backwards search
// is not necessary and minimum equals the
// already found maximum
if (indxOf1 < 1) {
distances[1] = distances[0];
return Math.min.apply(null, distances);
}
// backwards search
for (let i = indxOf1; i >= 0; i -= 1) {
if (arr[i] === 2) {
break;
}
distances[1] += arr[i] !== 2 ? 1 : 0;
}
return Math.min.apply(null, distances);
}
Something like this will do the job. You could make the code shorter but I've tried to make it clear. Once we find 1, start at that index and keep checking adjacent indices. We also do bounds checking to ensure we don't overflow either end.
function closest(arr) {
const index = arr.findIndex(n => n === 1);
const len = arr.length;
let offset = 1;
while (true) {
const before = index - offset;
const after = index + offset;
const beforeBad = before < 0;
const afterBad = after >= len;
// It's necessary to check both, we could exceed the bounds on one side but not the other.
if (beforeBad && afterBad) {
break;
}
if ((!beforeBad && arr[before] === 2) || (!afterBad && arr[after] === 2)) {
return offset;
}
++offset;
}
return -1;
}
You could approach this using entries and reduce.
const arr = [2, 0, 0, 0, 2, 2, 1, 0, 0 ,2];
const goal = arr.indexOf(1);
const indices = [];
// Find all the indices of 2 in the array
for (let x of arr.entries()) {
if (x[1] === 2) indices.push(x[0]) ;
}
// Find the index that is closest to your goal
const nearestIndex = indices.reduce((prev, curr) => {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
}); // 5
console.log(Math.abs(goal - nearestIndex)); // 1
How about this:
Output = Input.map((cur,idx,arr)=>cur==2?Math.abs(idx-arr.indexOf(1)):Infinity).sort()[0]
You could avoid for loops here in favor of a more functional style. The function minDist takes m, n, and array as arguments, and returns the minimum distance between the first occurrence of m and any occurrence of n in an array.
First, map is used to create an array with pairs for each element containing the distance to the target m element and the value of the current element. Then filter is used to keep only the pairs representing n elements. Then sort is used so that the pairs representing the closest elements are at the beginning of the array. Finally, the [0] pair of the sorted array represents the closest element, and the [0] element of this closest pair is the minimum distance.
function minDist(m, n, array) {
let index = array.indexOf(m);
return array
.map((x, i) => [Math.abs(i - index), x])
.filter(p => p[1] === n)
.sort()[0][0];
}
console.log(minDist(1, 2, [1, 0, 0, 0, 2, 2, 2]));
console.log(minDist(1, 2, [2, 0, 0, 0, 2, 2, 1, 0, 0, 2]));
Without using .indexOf, I'm trying to loop through an array from the end to find the last occurrence of a specific value. This is what I have so far but it keeps returning undefined.
var lastIndexOf = function (array, index) {
for (i=array.length-1; i < 0; i--) {
if (array[i] === index) {
return i;
}
}
}
console.log(lastIndexOf([ 0, 1, 4, 1, 2 ], 1); //should return 3
console.log(lastIndexOf([ 0, 1, 4, 1, 2 ], 2); //should return 4
Yor check is wrong, i is never smaller than zero for an index.
By the way, if you check against a value, you could use the name value instead of index, which leads to wrong assumptions and declare all variables in advance.
var lastIndexOf = function(array, value) {
var i;
for (i = array.length - 1; i >= 0; i--) {
if (array[i] === value) {
return i;
}
}
return -1; // standard default value for not found indices
};
console.log(lastIndexOf([0, 1, 4, 1, 2], 1)); // 3
console.log(lastIndexOf([0, 1, 4, 1, 2], 2)); // 4
console.log(lastIndexOf([0, 1, 4, 1, 2], 42)); // -1
A shorter approach with a while loop.
var lastIndexOf = function(array, value) {
var i = array.length
while (i--) {
if (array[i] === value) {
return i;
}
}
return -1;
};
console.log(lastIndexOf([0, 1, 4, 1, 2], 1)); // 3
console.log(lastIndexOf([0, 1, 4, 1, 2], 2)); // 4
console.log(lastIndexOf([0, 1, 4, 1, 2], 42)); // -1
Your loop condition is wrong. i will never be less than 0 unless the array is empty.
Use this instead:
(i=array.length-1; i >= 0; i--) {
You should change the check you make in the for statement. We start from the last index and we go down to the 0 index. So you loop while index >=0 and not when index<0.
I used different names for some parameters and arguments, in order to make the code more readable.
var lastIndexOf = function (array, number) {
for (var index = array.length-1; index>=0; index--) {
if (array[index] === number) {
return index;
}
}
};
console.log(lastIndexOf([ 0, 1, 4, 1, 2 ], 1)); //should return 3
Question has been moved to CodeReview: https://codereview.stackexchange.com/questions/154804/find-a-list-of-objects-in-an-array-with-javascript
Having an array of objects - such as numbers - what would be the most optimal (Memory and CPU efficiency) way if finding a sub group of objects? As an example:
demoArray = [1,2,3,4,5,6,7]
Finding [3,4,5] would return 2, while looking for 60 would return -1.
The function must allow for wrapping, so finding [6,7,1,2] would return 5
I have a current working solution, but I'd like to know if it could be optimized in any way.
var arr = [
1,
5,2,6,8,2,
3,4,3,10,9,
1,5,7,10,3,
5,6,2,3,8,
9,1]
var idx = -1
var group = []
var groupSize = 0
function findIndexOfGroup(g){
group = g
groupSize = g.length
var beginIndex = -2
while(beginIndex === -2){
beginIndex = get()
}
return beginIndex
}
function get(){
idx = arr.indexOf(group[0], idx+1);
if(idx === -1 || groupSize === 1){
return idx;
}
var prevIdx = idx
for(var i = 1; i < groupSize; i++){
idx++
if(arr[getIdx(idx)] !== group[i]){
idx = prevIdx
break
}
if(i === groupSize - 1){
return idx - groupSize + 1
}
}
return -2
}
function getIdx(idx){
if(idx >= arr.length){
return idx - arr.length
}
return idx
}
console.log(findIndexOfGroup([4,3,10])) // Normal
console.log(findIndexOfGroup([9,1,1,5])) // Wrapping
You could use the reminder operator % for keeping the index in the range of the array with a check for each element of the search array with Array#every.
function find(search, array) {
var index = array.indexOf(search[0]);
while (index !== -1) {
if (search.every(function (a, i) { return a === array[(index + i) % array.length]; })) {
return index;
}
index = array.indexOf(search[0], index + 1);
}
return -1;
}
console.log(find([3, 4, 5], [1, 2, 3, 4, 5, 6, 7])); // 2
console.log(find([6, 7, 1, 2], [1, 2, 3, 4, 5, 6, 7])); // 5
console.log(find([60], [1, 2, 3, 4, 5, 6, 7])); // -1
console.log(find([3, 4, 5], [1, 2, 3, 4, 6, 7, 3, 4, 5, 9])); // 6
.as-console-wrapper { max-height: 100% !important; top: 0; }
My take on the problem is to use slice() and compare each subarray of length equal to the group's length to the actual group array. Might take a bit long, but the code is short enough:
// The array to run the tests on
var arr = [
1,
5, 2, 6, 8, 2,
3, 4, 3, 10, 9,
1, 5, 7, 10, 3,
5, 6, 2, 3, 8,
9, 1
];
// Check arrays for equality, provided that both arrays are of the same length
function arraysEqual(array1, array2) {
for (var i = array1.length; i--;) {
if (array1[i] !== array2[i])
return false;
}
return true;
}
// Returns the first index of a subarray matching the given group of objects
function findIndexOfGroup(array, group) {
// Get the length of both arrays
var arrayLength = array.length;
var groupLength = group.length;
// Extend array to check for wrapping
array = array.concat(array);
var i = 0;
// Loop, slice, test, return if found
while (i < arrayLength) {
if (arraysEqual(array.slice(i, i + groupLength), group))
return i;
i++;
}
// No index found
return -1;
}
// Tests
console.log(findIndexOfGroup(arr,[4,3,10])); // Normal
console.log(findIndexOfGroup(arr,[9,1,1,5])); // Wrapping
console.log(findIndexOfGroup(arr,[9,2,1,5])); // Not found
If the group is longer than the array, some errors might occur, but I leave it up to you to extend the method to deal with such situations.
How can i check an array if its elements are all 0's except one which is 1?
sample:
array = [0, 0, 0, 1, 0];
check(array); //returns the index where it is 1 which is 3
array = [0, 3, 0, 2, 0];
check(array); //returns -1
array = [0, 3, 1, 2, 0];
check(array); //returns -1 again if there are non zero aside from 1, it should be one 1 and others are all 0.
array = [0, 0, 1, 0, 1];
check(array); //returns -1 again, there should just be one element of 1
function check(a) {
var index = -1;
for (var i = 0; i < a.length; i++) {
if (a[i] == 1) {
if (index < 0) {
index = i;
} else {
return -1;
}
} else if (a[i] != 0) {
return -1;
}
}
return index;
}
array1 = [0, 0, 0, 1, 0];
check(array1); //returns 3
array2 = [0, 3, 0, 2, 0];
check(array2); //returns -1
You can use a couple of filters, to generate an array of invalid numbers (i.e. not 0 or 1), and then an array of ones - from the original array. In the end, you can check the lengths of these resultant arrays to see if your criteria is met.
var others = a.filter(function(_item) { return (_item !== 1 && _item !== 0); }),
ones = a.filter(function(_item) { return (_item === 1); });
if(others.length === 0 && ones.length === 1) {
// valid
}
If array elements are guaranteed to be non-negative , then you can sum all elements of array. If sum is anything other than 1, it is not your desired array.
You don't have to loop array elements to calculate sum of elements. Use new reduce function of JavaScript Array. Look it up on web.
Things can get complicated , if array elements can be negative as well.