Compare 2 list get the moving id + offset + direction - javascript

I would like to compare 2 lists and get 3 variables: moving item, offset, direction.
Example 1:
Old list = 121,120,119,43,42,41,40,39,38,37
New list = 43,121,120,119,42,41,40,39,38,37
I move item 43 up to the front of list,
moving item = 43
offset = 3 (between old and new position have 3 items)
direction = +1 (as it move up)
Example 2
Old nids= 121,120,119,43,42,41,40,39,38,37
New nids= 121,120,119,43,41,40,39,38,37,42
I move item 42 down at the end of list,
moving item = 42
offset = 5 (between old and new position have 5 items)
direction = -1 (as it move down)
I am using javascript to handle it. Which algorithm I can use? Or what is the hints for me to search in google ? Thank you very much!

Here's something tested only with your examples. Perhaps it can at least provide one possible direction. (I may have left out some boundary/special cases.)
var a1 = [121,120,119,43,42,41,40,39,38,37],
b1 = [43,121,120,119,42,41,40,39,38,37],
a2 = [121,120,119,43,42,41,40,39,38,37],
b2 = [121,120,119,43,41,40,39,38,37,42];
function f(a,b){
var item,
i = offset = 0,
direction;
while (a[i] == b[i]){
i++;
}
if (a[i + 1] == b[i]){
direction = -1;
item = a[i];
while (b[i] != item){
offset++;
i++;
}
} else {
direction = 1;
item = b[i];
while (a[i] != item){
offset++;
i++;
}
}
return [item,offset,direction]
}
Output:
console.log(f(a1,b1))
Array [ 43, 3, 1 ]
console.log(f(a2,b2))
Array [ 42, 5, -1 ]

Related

Next Greatest Alphabet in an Array [Using Binary Search Algorithm]

I am trying to solve a question on Leetcode
Find Smallest Letter Greater Than Target
Given a characters array letters that is sorted in non-decreasing order and a character target, return the smallest character in the array that is larger than target.
Note that the letters wrap around.
For example, if target == 'z' and letters == ['a', 'b'], the answer is 'a'.
Example 1:
Input: letters = ["c","f","j"], target = "a"
Output: "c"
Example 2:
Input: letters = ["c","f","j"], target = "c"
Output: "f"
Example 3:
Input: letters = ["c","f","j"], target = "d"
Output: "f"
Constraints:
2 <= letters.length <= 104
letters[i] is a lowercase English letter.
letters is sorted in non-decreasing order.
letters contains at least two different characters.
target is a lowercase English letter.
My solution until now looks something like this:
function nextGreatestAlphabet(letters, target){
let left = 0;
let right = letters.length - 1;
let res = -1;
while(left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if (letters[mid] == target ) { console.log(1)
res = letters[mid];
left = mid + 1;
}
if(letters[mid] > target){ console.log(2)
right = mid -1;
res = letters[mid];
}
if(letters[mid] < target ){ console.log(3)
left = mid + 1;
}
}
return res;
}
console.log(nextGreatestAlphabet(["c","f","j"],"c")); //c
But the correct result should be "f" as c is already present here and next greatest is f
You should avoid setting res during the search, and certainly not set it when letters[mid] == target, as it is sure that is not the correct answer. Instead you should get the result after the loop has exited.
This then also means you don't need a separate case for letters[mid] == target, as the rest of the action is exactly what is done for letters[mid] < target. So you can make this a quite simple if (letters[mid] > target) ... else ... structure.
After the loop has exited, the left index points to the desired character. Then you need to deal with the boundary case where that index points beyond the array and map it to 0. This you can do with the remainder operator:
NB: I use here the name of the function as required by the LeetCode challenge:
function nextGreatestLetter(letters, target) {
let left = 0;
let right = letters.length - 1;
while(left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if(letters[mid] > target) {
right = mid -1;
} else {
left = mid + 1;
}
}
return letters[left % letters.length];
}
Personally, I prefer working with right being the index just after the range that is under consideration, and for deriving mid you can use the >> operator (Array sizes are limited in this challenge, so that is a safe operation):
function nextGreatestLetter(letters, target) {
let left = 0;
let right = letters.length;
while (left < right) {
let mid = (left + right) >> 1;
if (letters[mid] > target) {
right = mid;
} else {
left = mid + 1;
}
}
return letters[left % letters.length];
}
I think that your
if (letters[mid] == target ) { console.log(1)
res = letters[mid];
left = mid + 1;
}
sets the result to the target but you actually want to have the next one, so it should be sth. like
res=letter[mid+1].
if
mid==letters.length - 1
holds then it is already the right end. then you can just put
res = letter[0];

Get window of positions around a center point js

I am trying to figure out how to get a window of positions based off of a center point in JavaScript.
Say I have the following array:
[0,1,2,3,4,5,6,7,8,9]
If I wanted to get position 5 as the centre point, I'd also like to get positions 3,4,6 and 7 which would produce [3,4,5,6,7].
However, I'm also trying to get it where when it's within reach of the left or right bound of the array, it pushes the window in the opposite direction.
For example if I wanted position 1 I'd like the array to return as [0,1,2,3,4].
Or if I wanted position 0, I'd like the array as [0,1,2,3,4].
The same applies for the end of the array, e.g. I want to get position 8 so I'd return [5,6,7,8,9].
I'm struggling to get this into JavaScript and I feel I am overcomplicating the matter.
My current code is as follows however I am not attached to this code at all so completely changing it is fine:
positions() {
let left = 0;
let right = 5;
let middle = this.steps.length / 2;
// Closer to left.
if (this.index < middle) {
if (this.steps[this.index - 2]) {
left = this.index - 2;
right = this.index + 2;
}
else if (this.steps[this.index - 1]) {
left = this.index - 1;
right = this.index + 3;
}
}
// Closer to right.
else if (this.index > middle) {
if (this.steps[this.index + 3]) {
left = this.index - 2;
right = this.index + 3;
}
else if (this.steps[this.index + 2]) {
left = this.index - 3;
right = this.index + 2;
}
else if (this.steps[this.index + 1]) {
left = this.index - 4;
right = this.index + 1;
}
}
else {
left = this.index - 2;
right = this.index + 3;
}
return { left, right };
},
You can employ some maths to calculate what you get.
The start index is the centre point minus the span you want. E.g, centre 5 and span of 2 produces a start position of 5 - 2 = 3.
The end index is the centre point plus the span. E.g., centre 5 and span of 2 produces an end position of 5 + 2 = 7.
[0,1,2,3,4,5,6,7,8,9]
^ ^ ^
| | |
start -+ | |
centre +---+ |
end ---+-------+
| |
[3,4,5,6,7]
To handle the "overflow" you can clamp the start/end values.
start cannot be lower than the start of the array (which is 0).
while end can be at most the last position of the array (which is arr.length - 1).
If the start or end position forms less span than the expected one, the leftover can be transferred to the opposite index:
With centre of 1 and span of 2:
[0,1,2,3,4,5,6,7,8,9]
^ ^ ^
| | |
start --+ | |
centre ---+ |
padded end -----+
With centre of 8 and span of 2:
[0,1,2,3,4,5,6,7,8,9]
^ ^ ^
| | |
padded start -----+ | |
centre -----------------+ |
end ----------------------+
Finally, the only situation left to handle is what happens if the array is simply not big enough. That's up to you, just returning the whole array is sensible but also you may choose to throw an error or an empty array. If you are sure it's never going to happen, you can also leave it unhandled.
Here is how the implementation can look
function getRange(arr, pos, span) {
if ((span*2 + 1) > arr.length) {
throw Error("not enough items in array"); //or just return arr; or return []; etc.
}
let start = Math.max((pos - span), 0);
let end = Math.min((pos + span), arr.length - 1);
const leftoverStart = span - (pos - start);
const leftoverEnd = span - (end - pos);
if (leftoverStart) {
end += leftoverStart;
} else if (leftoverEnd) {
start -= leftoverEnd;
}
return arr.slice(start, end+1);
}
const arr = [0,1,2,3,4,5,6,7,8,9];
console.log(getRange(arr, 5, 2));
console.log(getRange(arr, 1, 2));
console.log(getRange(arr, 8, 2));
let array = [0,1,2,3,4,5,6,7,8,9]
const getSlice = (arr,index) => {
let leftSide = index - 2;
let rightSide = index + 2;
if(leftSide<0){
return arr.slice(0,5)
}
if(rightSide >= arr.length){
return arr.slice(arr.length-5)
}
return arr.slice(index-2,index+3)
}
console.log(getSlice(array,0))
console.log(getSlice(array,1))
console.log(getSlice(array,2))
console.log(getSlice(array,4))
console.log(getSlice(array,7))
console.log(getSlice(array,8))
console.log(getSlice(array,9))
var steps = [0,1,2,3,4,5,6,7,8,9];
var res = [];
var i = 3; // index // or 0, 9, 8, 2, 1
var mid = steps.length / 2;
var lenghOfResult = 5;
var left = 0;
var right = 0;
function computed() {
res = [];
res.push(i);
left = i;
right = i;
for(let c=1;c<=lenghOfResult;c++) {
if(steps.hasOwnProperty(i-c) && (i-c <= i-1) && res.length <lenghOfResult)
{
left = i-c;
res.unshift((i-c));
}
if(steps.hasOwnProperty(i+c) && (i+c >= i) && res.length <lenghOfResult) {
right = i+c;
res.push((i+c));
}
}
console.log('left', left, 'right', right);
}
computed();
console.log(res);

Not sure what I am doing wrong in binary search

I have a binary search and I tried to paper trace it, in my account it should return the value as true in the second iteration of the while loop, only the base case is running in console.log , I have no idea what I am doing wrong here, would love some pointers
const binarySearch = (sortedArray,value ) =>{
let left =0
let right = sortedArray.length-1
let middle =left+Math.floor((right-left ) /2)
// console.log(`
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// left ==${left}
// right ==${right}
// middle==${middle}
// is value === middle??? ${value === sortedArray[middle]}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// `)
while(left<right){
if(sortedArray[middle] === value){
return middle
}
if(sortedArray[middle] < value){
left = middle+1
}
if(sortedArray[middle] > value){
right = middle-1
}
}
return -1
}
binarySearch([1,2,3,4,5], 4)
edit:I found the bug! I thought for some strange reason that the while loop would use the updated middle value, but there is no reason for it to, as javascript just keeps going on the call stack unless specifically I modify some value. Then I had another issue where I was not accounting if left or right could be the value, it seems to work now, would love some tips for improvement or advice, thanks.
const binarySearch = (sortedArray,value ) =>{
let left =0
let right = sortedArray.length-1
while(left<=right){
let middle =left+Math.floor((right-left ) /2)
if(sortedArray[middle] === value){
return middle
}
if(sortedArray[middle] < value){
left = middle+1
}
if(sortedArray[middle] > value){
right = middle-1
}
}
return -1
}
binarySearch([1,2,3,4,5], 1)
So the main issue I see over here is that you're not updating your middle pointer every time you're updating the left and right pointers... You have to figure out what's the new middle pointer of either of the sub arrays:
const binarySearch = (sortedArray, value) => {
let left = 0;
let right = sortedArray.length - 1;
let middle = left + Math.floor((right - left) / 2);
while (left <= right) {
// The part you're missing
middle = left + Math.floor((right - left) / 2);
if (sortedArray[middle] === value) {
return middle;
}
if (sortedArray[middle] < value) {
left = middle + 1;
}
if (sortedArray[middle] > value) {
right = middle - 1;
}
}
return -1;
};
console.log(binarySearch([1, 2, 3, 4, 5], 4));

Grouping Numbers JS algorithm

I was trying to solve a problem:
Problem:
Given an array of Positive repetitive numbers. Output
should give an array with odds sorted on the right and evens on the
left (no particular order)
Input : [4,1,2,3,4]
Output: [4,2,3,1]
Solve it In-place and without using extra space and O(N) runtime.
Code:
/*
* Algorithm is simple, have two pointers one on the left and another on the right.
* Note: We are sorting all evens on the left and odds on the right
* If you see even on left, move on else swap.
*/
function groupNumbers(intArr) {
if(intArr.length == 0 || intArr.length == 1){
return intArr;
}
for(let i=0, j =intArr.length-1; i<intArr.length; i++){
if(j>=i){ //elements should not overlap
let start = intArr[i];
let end = intArr[j];
if(start%2 == 0){ //Even
i++;
} else {
[start, end] = [end, start]; //swap
}
if(end%2 == 1){
j--;
} else {
[start, end] = [end, start]; //swap
}
} //if-ends
}//for-ends
return intArr;
}
I'm not sure where I'm going wrong. I'm missing something. I'm getting the same sorted array as output.
Condition: **SOLVE it INPLACE and without using extra space ** (Preferably in ONE iteration)
I'm not sure where I'm going wrong. I'm missing something. I'm getting the same sorted array as output.
several things:
let start = intArr[i];
let end = intArr[j];
...
[start, end] = [end, start];
this does indeed swap the values in the variables start and end, but not the indices in the Array.
Then you have two i++ in the same loop that increment the left pointer.
if(start%2 == 0){ //Even
i++;
} else {
[start, end] = [end, start]; //swap
}
here you swap the items when the left pointer points to an odd value, but there's no check that the right pointer also points to an even value. you might as well just swap two odd values here. Same for the right pointer.
const isEven = v => (v&1) === 0;
const isOdd = v => (v&1) === 1;
function groupNumbers(arr){
var left = 0, right = arr.length-1;
while(left < right){
//move the left pointer to find the next odd value on the left
while(left < right && isEven(arr[left])) ++left;
//move the right pointer to find the next even value on the right
while(left < right && isOdd(arr[right])) --right;
//checking that the two pointer didn't pass each other
if(left < right) {
console.log("swapping %i and %i", arr[left], arr[right]);
//at this point I know for sure that I have an odd value at the left pointer
//and an even value at the right pointer
//swap the items
var tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
return arr;
}
[
[1,2,3,4],
[1,2,3,4,5,6,7,8,9,0],
[1,3,5,7],
[2,4,1,3],
[5,4,3,2,1],
].forEach(sequence => {
console.log("\ninput: " + sequence);
console.log("output: " + groupNumbers(sequence));
});
.as-console-wrapper{top:0;max-height:100%!important}
as suggested by #JaredSmith, the same thing just using a sort-function :)
function sortEvenLeftOddRight(a,b){
return (a&1) - (b&1);
//return (a&1) - (b&1) || a-b; //to additionally sort by value
}
[
[1,2,3,4],
[1,2,3,4,5,6,7,8,9,0],
[1,3,5,7],
[2,4,1,3],
[5,4,3,2,1],
].forEach(sequence => {
console.log("\ninput: " + sequence);
sequence.sort(sortEvenLeftOddRight);
console.log("output: " + sequence);
});
.as-console-wrapper{top:0;max-height:100%!important}
A very concise method to solve this would be to use reduce:
const out = arr.reduce((p, c) => {
// if the value is divisible by 2 add it
// to the start of the array, otherwise push it to the end
c % 2 === 0 ? p.unshift(c) : p.push(c)
return p;
}, []);
OUT
[4,2,4,1,3]
DEMO

javascript grid help

I am working on looping through a few grid items and was hoping someone could help me find a way to figure out how to get to the items that I need and possibly I will better understand how to access the items in the grid. So here is the grid.
[0] [1] [2] [3] [4]
[5] [6] [7] [8] [9]
[10] [11] [12] [13] [14]
[15] [16] [17] [18] [19]
[20] [21] [22] [23] [24]
This is basically a 5x5 grid, however it could be any size but I just used this for the example. There are two ways I would like to loop through this. The first one being in this order:
0,1,2,3,4,9,14,19,24,23,22,21,20,15,10,5,6,7,8,13,18,17,16,11,12
Basically all that is doing is going around the outside starting from the top left. The next way I would want to loop through that is through the same exact values except in reverse order (basically inside out instead of outside in) and actually thinking about this now I could just loop through the first method backwards. If anyone can help me with this it would be great. I really want to learn more on how to loop through items in crazy arrangements like this.
This function
function n(i, w, h)
{
if (i < w)
return i;
if (i < w + h-1)
return w-1 + (i-w+1)*w;
if (i < w + h-1 + w-1)
return w-1 + (h-1)*w - (i - (w + h-1 - 1));
if (i < w + h-1 + w-1 + h-2)
return (h - (i - (w + w-1 + h-1 - 2)))*w;
var r = n(i - (w-1)*2 - (h-1)*2, w-2, h-2);
var x = r % (w-2);
var y = Math.floor(r / (w-2));
return (y+1)*w + (x+1);
}
accepts as input
i: Index of the item you're looking for
w: Width of the grid
h: Height of the grid
and returns the corresponding element of the grid assuming that clock-wise spiral traversal.
The implementation simply checks if we're on the top side (i<w), on the downward right side (i<w+h-1) and so on and for these cases it computes the cell element explicitly.
If we complete one single trip around the spiral then it calls recursively itself to find the element in the inner (w-2)*(h-2) grid and then extracts and adjusts the two coordinates considering the original grid size.
This is much faster for big grids than just iterating and emulating the spiral walk, for example computing n(123121, 1234, 3012) is immediate while the complete grid has 3712808 elements and a walk of 123121 steps would require quite a long time.
You just need a way to represent the traversal pattern.
Given an NxM matrix (e.g. 5x5), the pattern is
GENERAL 5x5
------- -------
N right 5
M-1 down 4
N-1 left 4
M-2 up 3
N-2 right 3
M-3 down 2
N-3 left 2
M-4 up 1
N-4 right 1
This says move 5 to the right, 4 down, 4 left, 3 up, 3 right, 2 down, 2 left, 1 up, 1 right. The step size shifts after each two iterations.
So, you can track the current "step-size" and the current direction while decrementing N, M until you reach the end.
IMPORTANT: make sure to write down the pattern for a non-square matrix to see if the same pattern applies.
Here's the "walking" method. Less efficient, but it works.
var arr = new Array();
for(var n=0; n<25; n++) arr.push(n);
var coords = new Array();
var x = 0;
var y = 0;
for(var i=0; i<arr.length; i++) {
if( x > 4 ) {
x = 0;
y++;
}
coords[i] = {'x': x, 'y': y};
x++;
}
// okay, coords contain the coordinates of each item in arr
// need to move along the perimeter until a collision, then turn.
// start at 0,0 and move east.
var dir = 0; // 0=east, 1=south, 2=west, 3=north.
var curPos = {'x': 0, 'y': 0};
var resultList = new Array();
for(var x=0; x<arr.length; x++) {
// record the current position in results
var resultIndex = indexOfCoords(curPos, coords);
if(resultIndex > -1) {
resultList[x] = arr[resultIndex];
}
else {
resultList[x] = null;
}
// move the cursor to a valid position
var tempCurPos = movePos(curPos, dir);
var outOfBounds = isOutOfBounds(tempCurPos, coords);
var itemAtTempPos = arr[indexOfCoords(tempCurPos, coords)];
var posInList = resultList.indexOf( itemAtTempPos );
if(outOfBounds || posInList > -1) {
dir++;
if(dir > 3) dir=0;
curPos = movePos(curPos, dir);
}
else {
curPos = tempCurPos;
}
}
/* int indexOfCoords
*
* Searches coordList for a match to myCoords. If none is found, returns -1;
*/
function indexOfCoords(myCoords, coordsList) {
for(var i=0; i<coordsList.length; i++) {
if(myCoords.x == coordsList[i].x && myCoords.y == coordsList[i].y) return i;
}
return -1;
}
/* obj movePos
*
* Alters currentPosition by incrementing it 1 in the direction provided.
* Valid directions are 0=east, 1=south, 2=west, 3=north
* Returns the resulting coords as an object with x, y.
*/
function movePos(currentPosition, direction) {
var newPosition = {'x':currentPosition.x, 'y':currentPosition.y};
if(direction == 0) {
newPosition.x++;
}
else if(direction == 1) {
newPosition.y++;
}
else if(direction == 2) {
newPosition.x--;
}
else if(direction == 3) {
newPosition.y--;
}
return newPosition;
}
/* bool isOutOfBounds
*
* Compares the x and y coords of a given position to the min/max coords in coordList.
* Returns true if the provided position is outside the boundaries of coordsList.
*
* NOTE: This is just one, lazy way of doing this. There are others.
*/
function isOutOfBounds(position, coordsList) {
// get min/max
var minx=0, miny=0, maxx=0, maxy=0;
for(var i=0; i<coordsList.length; i++) {
if(coordsList[i].x > maxx) maxx = coordsList[i].x;
else if(coordsList[i].x < minx) minx = coordsList[i].x;
if(coordsList[i].y > maxy) maxy = coordsList[i].y;
else if(coordsList[i].y < miny) miny = coordsList[i].y;
}
if(position.x < minx || position.x > maxx || position.y < miny || position.y > maxy) return true;
else return false;
}
This will move through the grid in a direction until it hits the bounds or an item already in the results array, then turn clockwise. It's pretty rudimentary, but I think it would do the job. You could reverse it pretty simply.
Here's a working example: http://www.imakewidgets.com/test/walkmatrix.html

Categories