Given this arrays I want to check if "sequence" is a subsequence of "array", meaning all the numbers exist in the original array and in the same order:
array = [5, 1, 22, 25, 6, -1, 8, 10];
sequence = [1, 6, -1, 10];
Not sure why my code doesn't work.
function isValidSubsequence(array, sequence) {
let seqIdx = 0;
let arrId = 0;
for (const value of sequence ){
if (seqIdx === sequence.length) break;
if (array[arrId] === value) {
seqIdx++;
arrId++;
}
}
return seqIdx === sequence.length
}
Your solution doesn't work because it never moves past the first entry in array. You never increment any of your indexes unless the sequence value matches array[arrId].
I'd use Array.prototype.indexOf() with the second fromIndex parameter to create a shrinking window of array as you search. If you ever reach an iteration of sequence that cannot be found, you know it fails the test
function isValidSubsequence(array, sequence) {
let index = -1;
for (const value of sequence) {
index = array.indexOf(value, index + 1); // find the next sequence value
if (index === -1) {
return false; // not found, return false immediately
}
}
return true;
}
const array = [5, 1, 22, 25, 6, -1, 8, 10];
const sequence = [1, 6, -1, 10];
console.log("valid sub-sequence:", isValidSubsequence(array, sequence));
console.log("out of order:", isValidSubsequence(array, [25, 22]));
console.log("unknown elements:", isValidSubsequence(array, [5, 11]));
Remove arrIdx.
In a for...of loop the index of array isn't needed in this case since value progresses on each iteration.
Remove the first flow control statement.
if (seqIdx === sequence.length) break;
There's no need to interupt the loop. The Boolean returned outside of the loop is sufficient.
Change the second flow control statement to monitor sequence[seqIdx] not array
if (sequence[seqIdx] === value) {
seqIdx++;
}
The key to this algorithm is to progress through array one number at a time (as is the norm), but not the sequence. The counter, seqIdx, only progresses on a match so basically if sequence ends before or at the end of the loop it is a valid subsequence.
const arr = [5, 1, 22, 25, 6, -1, 8, 10];
const seq = [1, 6, -1, 10];
function isValidSubsequence(array, sequence) {
let seqIdx = 0;
for (const value of array) {
if (sequence[seqIdx] === value) {
seqIdx++;
}
}
return seqIdx === sequence.length;
};
console.log(isValidSubsequence(arr, seq));
You can achieve it in a simple way by finding the index of sequence array elements from the original array and then check if the indexed array is sorted or not.
Demo :
const array = [5, 1, 22, 25, 6, -1, 8, 10];
const sequence = [1, 6, -1, 10];
// Find index of the elements from the original array.
const indexArr = sequence.map((item) => array.indexOf(item));
// Now test if this indexed array is sorted or not to check if sequence array having same order as per the original array.
function isSorted(arr) {
var i = 0;
var last = arr.length - 1;
return (function check() {
return (i >= last) || (arr[i] <= arr[++i] && check());
})();
}
console.log(isSorted(indexArr))
Related
This question already has answers here:
Check array in JS - is list sorted? [duplicate]
(9 answers)
Closed 7 months ago.
how can I determine when the first X numbers of an array is in order AND the last element in 0? i.e the array is
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0
I currently have this, but this relies on the array always being the same, which isn't very flexibile
const sorted = (array) => {
const solved = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0]
return (JSON.stringify(array) == JSON.stringify(solved))
}
You could do something as simple as this:
const checkArray = (arr) => {
if(arr[arr.length-1] != 0){
return false;
}
const nums = arr.slice(0, arr.length - 1);
const sortedArr = [...nums].sort((a, b) => a - b);
for (let i = 0; i < nums.length; i++) {
if(nums[i] != sortedArr[i]){
return false;
}
}
return true;
}
console.log(checkArray([1,2,3,0])); // true
console.log(checkArray([1,2,3,4])); // false
console.log(checkArray([1,3,2,0])); // false
Basically the steps are:
Check if last element is 0, else do an early return.
Create a sorted version of the first part of the array (the one with the numbers)
Check if the numbers part is equal to the sorted array. If any element is different return false
In the end you return true only if every condition is verified.
This is generic enough so that if in the future you want to change the sorting type you can just act on the sort function (for example if you want to make it descending).
First define a generic function to verify that a segment of an array is sorted, then define a second function that uses the first to see the first values are sorted, and add a check for the final value:
function isSegmentSorted(array, start=0, end=array.length) {
for (let i = start + 1; i < end; i++) {
if (array[i - 1] > array[i]) return false;
}
return true;
}
function isSortedWithExtraZero(array) {
return array.at(-1) === 0 &&
isSegmentSorted(array, 0, array.length - 1);
}
var array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0];
console.log(isSortedWithExtraZero(array));
We can use every function for that. With !idx we exclude the first index 0, then we check if idx is less than the length of your array. If so, check if it is sorted, else check it it equals to zero.
const solutions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0];
const sorted = solutions.every((val, idx, arr) =>
!idx || (idx < solutions.length - 1 ? arr[idx - 1] <= val : val === 0)
);
console.log(sorted);
const numbers = [2, 4, 5, 2, 3, 5, 1, 2, 4];
I need to create the function indexOfRepeatedValue (array). Use numbers that are stored in the variable numbers.
I should create a variable firstIndex in this function. In the for loop, check which number repeats first and assign its index to firstIndex. Then write this variable to the console - outside of the for loop.
I came up with this idea It doesn't work at all. I'm lost, some piece of advice?
const numbers = [2, 4, 5, 2, 3, 5, 1, 2, 4];
function indexOfRepeatedValue(array) {
let firstIndex;
for (let i = 0; i < array.length; i++)
if (firstIndex.indexOf(array[i]) === -1 && array[i] !== '');
firstIndex.push(array[i]);
return firstIndex;
}
console.log(
indexOfRepeatedValue(numbers)
)
Start by making firstIndex an array: let firstIndex = [];
Then make sure the i is not outside the scope you used since you use let.
You end the statement with a semicolon, that means the loop never does the next line
Then return the first number found that is in your new array
Note JS Arrays start at 0, so the result is 3, since the second number 2 is in the 4th place
I have kept my code as close to yours as possible.
const numbers = [2, 4, 5, 2, 3, 5, 1, 2, 4];
function indexOfRepeatedValue(array) {
let firstIndex = [];
for (let i = 0; i < array.length; i++) {
if (firstIndex.indexOf(array[i]) !== -1) { // we found it
console.log("found",array[i], "again in position", i)
console.log("The first of these is in position",numbers.indexOf(array[i]))
return i; // found - the function stops and returns
// return numbers.indexOf(array[i]) if you want the first of the dupes
}
firstIndex.push(array[i]); // not found
}
return "no dupes found"
}
console.log(
indexOfRepeatedValue(numbers)
)
There are many more ways to do this
Javascript: How to find first duplicate value and return its index?
You could take an object for storing the index of a value and return early if the index exist.
function indexOfRepeatedValue(array) {
let firstIndex = {};
for (let i = 0; i < array.length; i++) {
if (firstIndex[array[i]] !== undefined) return firstIndex[array[i]];
firstIndex[array[i]] = i;
}
return -1;
}
const numbers = [2, 4, 5, 2, 3, 5, 1, 2, 4];
console.log(indexOfRepeatedValue(numbers));
Start by initializing firstIndex:
let firstIndex = [];
Use the following to find the index of each repeated element:
if( array.slice(0,i).includes(array[i]) ) {
firstIndex.push( i );
}
If you need the absolute first index of a repeat:
return firstIndex[0];
//Please note that if this is your goal then you do not even need the variable firstIndex, nor do you need to run through the whole loop.
If you need indices of all repeated elements:
return firstIndex;
const numbers = [2, 4, 5, 2, 3, 5, 1, 2, 4];
function indexOfRepeatedValue(array) {
let firstIndex = [];
for (let i = 0; i < array.length; i++)
if( array.slice(0,i).includes(array[i]) ) {
firstIndex.push(i);
}
return firstIndex[0];
}
console.log(
indexOfRepeatedValue(numbers)
)
NOTE
Alternatively, you can use Array#map to get index of repeated values then use Array#filter to retain only those indices, the first [0] is what you're lookin for.
const numbers = [2, 4, 5, 2, 3, 5, 1, 2, 4];
const indexOfRepeatedValue = arr =>
arr.map((a,i) => arr.slice(0,i).includes(a) ? i : -1)
.filter(i => i > -1)[0];
console.log( indexOfRepeatedValue( numbers ) );
Have the function ArrayChallenge(arr) take the array of integers stored in arr, and determine if any two numbers (excluding the first element) in the array can sum up to the first element in the array. For example: if arr is [7, 3, 5, 2, -4, 8, 11], then there are actually two pairs that sum to the number 7: [5, 2] and [-4, 11]. Your program should return all pairs, with the numbers separated by a comma, in the order the first number appears in the array. Pairs should be separated by a space. So for the example above, your program would return: 5,2 -4,11
If there are no two numbers that sum to the first element in the array, return -1
Input: [17, 4, 5, 6, 10, 11, 4, -3, -5, 3, 15, 2, 7]
Output: 6,11 10,7 15,2
Final Output: --6--,--1----1-- --1--0,7 --1----5--,2
Input: [7, 6, 4, 1, 7, -2, 3, 12]
Output: 6,1 4,3
Final Output: --6--,--1-- 4,3
My approach
function ArrayChallenge(arr) {
var sum = []
for (var i = 0; i < arr.length; i++){
for (var j = i + 1; j < arr.length; j++){
if(arr.[i] + arr[j]=== )
}
}
// code goes here
return arr;
}
// keep this function call here
console.log(ArrayChallenge(readline()));
Can you please help me with this ?
Logic
Loop through the array.
Start from index 1 to last node (except index 0) in the outer loop.
Srart from one node next to the outer loop in the inner loop.
Check the sum of both nodes.
If the sum value is same as the node at first index, push that to sum array in required format.
Check the length of sum array. If length > 0 the join sum array and return. Else return -1
Working Code
const input = [17, 4, 5, 6, 10, 11, 4, -3, -5, 3, 15, 2, 7];
const input2 = [7, 6, 4, 1, 7, -2, 3, 12];
const input3 = [37, 6, 4, 1, 7, -2, 3, 12];
function ArrayChallenge(arr) {
var sum = []
for (var i = 1; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] + arr[j] === arr[0]) {
sum.push([arr[i], arr[j]].join());
}
}
}
return sum.length > 0 ? sum.join(" ") : -1;
}
console.log(ArrayChallenge(input));
console.log(ArrayChallenge(input2));
console.log(ArrayChallenge(input3));
Your approach uses a O(n^2) level complexity. This can be solved using O(n) if you're willing so sacrifice a little on space complexity.
What you can do is :
Make an empty object.
store all values of the array (not the 0th element) in the object as key and add it's value as true.
Loop the array (from 1st index). Take the value and subtract it from the 0th element. find this subtracted value from the object, If it does not return undefined, make a pair and save it.
One drawback of this method is, you'll find duplicate entries in the result.
This Approach uses O(n) Time complexity and O(n) space complexity
function ArrayChallange(arr) {
let numObj = {}
let i = 1
let result = []
let tempVal
// Pushing all elements of arr (from index 1) inside numObj
while(i<arr.length){
numObj[arr[i]] = true
}
i = 1
// Looping the array to find pairs
while(i < arr.length){
tempVal = numObj[Math.abs(arr[0] - arr[i])]
if(tempVal){
result.push(arr[i].toString() +","+tempVal.toString())
}
}
if(result.length !== 0)
return result.join(" ")
else
return -1
}
You could use a reducer followed by a forEach loop in order to push the pairs to an empty array, then join them at the end.
const ArrayChallenge = (nums) => {
const pairs = []
// Get the first and remove it from the array
const first = nums.splice(0, 1)[0]
nums.reduce((all, curr) => {
all.forEach((a) => {
// Check if we have a match
if (curr + a === first) {
// check if it's already in the array
// we don't want duplicates
if (pairs.indexOf(`${a},${curr}`) === -1 && pairs.indexOf(`${curr},${a}`) === -1) {
// push the pair to the array separated by a space
pairs.push(`${curr},${a}`)
}
}
})
return all
}, nums) // we pass in nums as the starting point
// If there are no pairs then return -1
if (pairs.length === 0) {
return -1
} else {
// Join the pairs together with a space
const result = pairs.join(' ')
// Replace each digit (\d) with hyphens before and after
const parsed = result.replace(/(\d)/g, '--$1--')
return parsed
}
}
const result1 = ArrayChallenge([17, 4, 5, 6, 10, 11, 4, -3, -5, 3, 15, 2, 7])
console.log(result1)
const result2 = ArrayChallenge([7, 6, 4, 1, 7, -2, 3, 12])
console.log(result2)
I have an array like:
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
How I can reorder this array with the following rules:
myArray[0][0] to reduce size to 2 elements (values 1,2 stay, 3,4 goes to next array)
keep all values just move extra array elements to the next array, but all arrays need to keep the current number of elements except last
WHat I already try is:
function conditionalChunk(array, size, rules = {}) {
let copy = [...array],
output = [],
i = 0;
while (copy.length)
output.push( copy.splice(0, rules[i++] ?? size) )
return output
}
conditionalChunk(myArray, 3, {0:2});
but in that case, I need to put rules for all arrays in the array, I need to know a number of elements for all arrays in the array, and that's what I want to avoid.
Is there any elegant way to do that?
The requirements were not that clear and I don't know why you would do any of that but here you have your solution: I have written a function that you can use to limit the cardinality of the subArrays.
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
function reorderArray(arr) {
let buff = [];
let newArr = [];
let maxSubarrCardinality = 2;
//flatMapping second level elements
//in a single level buffer
for (subArr of arr) {
for (elem of subArr) {
buff.push(elem);
}
}
//Inserting elements one at the time
//into a new array
for (elem in buff) {
//when the new array is empty
//push in the new array the first empty subArray
if (newArr.at(-1) == undefined)
newArr.push([]);
//if the last subArray has reached
//the maxCardinality push a new subArray
else if (newArr.at(-1).length >= maxSubarrCardinality) {
newArr.push([]);
newArr.at(-1).push(elem);
}
//if none of the previous special cases
//just add the element to the last subArray of the newArray
else {
newArr.at(-1).push(elem);
}
}
return newArr;
}
myArray = reorderArray(myArray);
Steps
I used Array#flat, so I had to keep track of the indices and length of each item.
let i = 0;
let itemLength = array[0]?.length;
After flattening the array, I use Array#reduce to loop through the items, setting the initialValue to an empty array.
I get the last item in the array and check if its length has reached the maximum allowed (which should be the one set in the rules argument for that index or the size argument).
If the max hasn't been reached, I push the current item into the last array. If it has, I create a new array, with the item as the only element
array.flat().reduce((acc, cur) => {
if (acc.length === 0) acc.push([]); // Just for the first iteration
if (acc.at(-1).length < (rules[i] ?? size)) acc[acc.length - 1].push(cur);
else acc.push([cur]);
If then either decrement the value of itemLength, or set it to the length of the next array. and increment the i variable to the next index
itemLength = itemLength === 0 ? (array[++i] ?? []).length : --itemLength;
let array = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
function conditionalChunk(array, size, rules = {}) {
let i = 0;
let itemLength = array[0]?.length;
return array.flat().reduce((acc, cur) => {
if (acc.length === 0) acc.push([]); // Just for the first iteration
if (acc.at(-1).length < (rules[i] ?? size)) acc[acc.length - 1].push(cur);
else acc.push([cur])
itemLength = itemLength === 0 ? (array[++i] ?? []).length : --itemLength;
return acc;
}, []);
}
console.log(JSON.stringify(conditionalChunk(array, 3, { 0: 2 })));
Here is what I came up with. Paste the code into the chrome console and test it.
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
//Getting initial lengths of inner arrays [4, 2, 3, 1]
var lengths = [];
myArray.forEach((arr) => {lengths.push(arr.length);});
// Extracting the elements of all the inner arrays into one array.
var allElements = [].concat.apply([], myArray);
// Updating the lengths of first and last inner arrays based on your requirement.
var firstArrLen = 2;
var lastArrLen = lengths[lengths.length -1] + (lengths[0] - 2)
lengths[0] = firstArrLen;
lengths[lengths.length -1] = lastArrLen;
// Initializing the final/result array.
var finalArr = [];
// Adding/Pushing the new inner arrays into the finalArr
for(var len of lengths) {
var tempArr = [];
for(var i=0; i<len; i++) {
tempArr.push(allElements[i]);
}
finalArr.push(tempArr);
for(var i=0; i<len; i++) {
// removes the first element from the array.
allElements.shift();
}
}
console.log(finalArr);
I have 2 questions, how can I get value instead of value inside array and how can I make this code shorter and declarative.
arr = [16, 4, 11, 20, 2]
arrP = [7, 4, 11, 3, 41]
arrTest = [2, 4, 0, 100, 4, 7, 2602, 36]
function findOutlier(arr) {
const isPair = (num) => num % 2 === 0
countEven = 0
countOdd = 0
arr1 = []
arr2 = []
const result = arr.filter((ele, i) => {
if (isPair(ele)) {
countEven++
arr1.push(ele)
} else {
countOdd++
arr2.push(ele)
}
})
return countEven > countOdd ? arr2 : arr1
}
console.log(findOutlier(arrTest))
Filtering twice may be more readable.
even = arr.filter((x) => x % 2 == 0);
odd = arr.filter((x) => x % 2 == 1);
if (even.length > odd.length) {
return even;
} else {
return odd;
}
If you're looking to do this with one loop, consider using the array reduce method to put each number into an even or odd bucket, and then compare the length of those buckets in your return:
function findOutlier(arr) {
const sorted = arr.reduce((acc, el) => {
acc[el % 2].push(el);
return acc;
},{ 0: [], 1: [] })
return sorted[0].length > sorted[1].length ? sorted[1] : sorted[0];
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(findOutlier(arr));
Note that this does not handle when the arrays are the same length gracefully (right now it'll just return the odd array).
You could take an object with the wanted part for collecting and add a short circuit if one of the types has a count of one and the others have a count greater than one.
const
isPair = num => num % 2 === 0,
findOutlier = array => {
count = { true: [], false: [] };
for (const value of array) {
count[isPair(value)].push(value);
if (count.true.length === 1 && count.false.length > 1) return count.true[0];
if (count.false.length === 1 && count.true.length > 1) return count.false[0];
}
};
console.log(...[[16, 4, 11, 20, 2], [7, 4, 11, 3, 41], [2, 4, 0, 100, 4, 7, 2602, 36]].map(findOutlier));
Here is an solution that selects the even or odd array based on the modulo result.
function findOutlier(integers) {
const even = [], odd = [], modulos = [even, odd];
for (const integer of integers) {
modulos[Math.abs(integer % 2)].push(integer);
}
return even.length > odd.length ? odd : even;
}
console.log(findOutlier([2, 4, 0, 100, 4, 7, 2602, 36]));
You unfortunately do need Math.abs() to handle negative values, because -3 % 2 == -1.
See: JavaScript % (modulo) gives a negative result for negative numbers
However the name findOutlier lets me assume there is only a single outlier within the provided list. If this is the case you can optimize the algorithm.
function findOutlier(integers) {
// With less than 3 integers there can be no outlier.
if (integers.length < 3) return;
const isEven = (integer) => integer % 2 == 0;
const isOdd = (integer) => !isEven(integer);
// Determine the outlire based on the first 3 elements.
// If there are 0 or 1 integers even, the outlire is even.
// if there are 2 or 3 integers even, the outlier is odd.
const outlier = integers.slice(0, 3).filter(isEven).length < 2
? isEven
: isOdd;
return integers.find(outlier);
}
console.log(findOutlier([2, 4, 0, 100, 4, 7, 2602, 36]));
You can do this without creating intermediate arrays by simply comparing each element to its neighbors and returning that element if it is different to both, or undefined if no outliers are found. This returns in the same iteration in which the outlier is first encountered, and returns the value itself and not an array.
function findOutlier(array) {
const
len = array.length,
isEven = (n) => n % 2 === 0;
for (const [i, value] of array.entries()) {
let
prev = array[(i-1+len)%len], // loop around if < 0 (first element)
next = array[(i+1)%len]; // loop around if >= length (last element)
if (isEven(value) !== isEven(prev) && isEven(value) !== isEven(next)) {
return value;
}
}
return undefined;
}
const arrays = [[16, 4, 11, 20, 2], [7, 4, 11, 3, 41], [2, 4, 0, 100, 4, 7, 2602, 36]]
console.log(...arrays.map(findOutlier));
Now that OP clarified the requirements (at least in a comment) this allows a different approach:
function findOutlier(array) {
let odd = undefined, even = undefined;
for (let i of array) {
let isEven = i % 2 == 0;
if (odd !== undefined && even !== undefined)
return isEven ? odd : even;
if (isEven) even = i;
else odd = i;
}
if (odd !== undefined && even !== undefined)
return array[array.length-1];
}
console.log(findOutlier([2,4,6,8,10,5]))
The algorithm will iterate the array, and store the lastest found odd and even numbers, respectively.
If we discovered both an odd and an even number already, with the current number we can decide, which of them is the outlier: If the current number is even, it's at least the second even number we found. Thus, the found odd number must be the outlier. The same applies vice versa if the current number is odd. The special case, if the outlier is the last element of the array, is checked with an additional condition after the loop.
If all numbers are odd or even (ie there is no outlier) this function will return undefined. This algorithm does not throw an error, if the preconditions are not met, ie if there is more than one outlier.