How to determine which element was moved in an array? - javascript

I am trying to find the difference between two arrays, by finding out which element was moved. I know that one element exactly will be moved and that the order will be maintained for the rest of the list, but am unable to figure out how to find this.
Example:
A: 1 2 3 4 5 6
B: 2 3 4 5 1 6
All of the elements exist in both lists, but how do I find out that the element 1 moved from index 0 to index 4?
My basic approach that I took but is not working is:
//Original array
var a = [1, 2, 3, 4, 5, 6];
//New array
var b = [2, 3, 4, 5, 1, 6];
for(var i=0; i < a.length; i++) {
if(a[i] != b[i] && a[i+1] != b[i]) {
console.log(b[i] + " moved");
}
}
I have fixed by code to print out b[i] instead of a[i], but it is not working in all cases such as:
A: 1, 2, 3, 4
B: 1, 4, 2, 3

The problem is with the second condition in your if statement. In your example, when element a[0] has moved, a[0+1] === b[0], so the if clause evaluates to false.
Try instead,
var idx = 0;
var len = a.length;
while ((a[idx] === b[idx] || a[idx] === b[idx+1]) && idx < len) {
idx++;
}
console.log('Element a[' + idx + ']=' + a[idx] + ' moved.');

basically, if I understand correctly, an element moving means it is deleted and inserted
somewhere else.
so you first find the first point where there was a deletion/insertion:
function whichMoved(a, b) {
for(var i=0; i < a.length; i++) {
if (a[i] != b[i]) {
now, if it was a deletion, then the element has been moved forward, meaning, inserted in a greater index in b, and all elements between the indices are shifted to the left,
meaning the the next element has moved one place to backward:
if(a[i+1] == b[i]) {
console.log(a[i] + " moved forward");
break;
}
otherwise, the element was moved backward:
else {
console.log(b[i] + " moved backward")
break;
}
the whole thing:
//Original array
var a = [1, 2, 3, 4, 5, 6];
//testing
whichMoved(a, [2,3,4,5,1,6]); //prints 1 moved forward
whichMoved(a, [5,1,2,3,4,6]); //prints 5 moved backward
function whichMoved(a, b) {
for(var i=0; i < a.length; i++) {
if (a[i] != b[i]) {
if(a[i+1] == b[i]) {
console.log(a[i] + " moved forward");
break;
} else {
console.log(b[i] + " moved backward")
break;
}
}
}
}

You can use jQuery .inArray() it will return the index, starting at 0 and returns -1 if not found:
var a = [1, 2, 3, 4, 5, 6];
//New array
var b = [2, 3, 4, 5, 1, 6];
for(i=0; i < a.length; i++) {
var j = $.inArray(a[i], b);
if(i != j){
console.log(a[i], "moved to index "+j);
}else{
console.log(a[i], "not moved");
}
}
See this jsfiddle: http://jsfiddle.net/Rdzj4/

Edited- probably not needed, but I hate to leave a wrong answer.
Here I look at the distance each item is from its original index,
and figure the one that is most out of order is the mover-
This assumes in [2,1,3,4,5,6] it is the two that moved, not the 1,
and in [1,2, 3, 4, 6, 5] it is the 6, not the 5.
function whoMoved(a, b){
var max= 0, min= a.length, dist,
order= b.map(function(itm, i){
dist= i-a.indexOf(itm);
if(dist<min) min= dist;
if(dist>max) max= dist;
return dist;
});
if(Math.abs(min)>= max) max= min;
return b[order.indexOf(max)];
}
//test
var a= [1, 2, 3, 4, 5, 6];
var b= [1, 6, 2, 3, 4, 5];//6 to left
var c= [1, 3, 4, 2, 5, 6];//2 to to right
var d= [3, 1, 2, 4, 5, 6];//3 to left
var e= [2, 3, 4, 5, 1, 6];//1 to right
[whoMoved(a, b), whoMoved(a, c), whoMoved(a, d),whoMoved(a, e)];
/* returned value: (Array) [6,2,3,1] */

Related

remove duplicates and sort an array with single loop in javascript

I want to write an algorithm to remove duplicates and sort an array with single loop in javascript. I want to do it with algorithms not declarative methods.
I can sort it with single loop but can't remove duplicates. I will be glad if someone help me.
Number of elements in array is 10^6, each element is equal to or greater than 0, less than 300
This is my sorting algorithm:
var a = [2, 7, 5, 1, 3, 2, 7];
for (let i = 0; i < a.length - 1; i++) {
if (a[i] > a[i + 1]) {
let temp = a[i]
a[i] = a[i + 1]
a[i + 1] = temp
i = -1
}
}
console.log(a)
It's honorable that you was curious enough to try to find a solution, even after the interview. A good sign for a programmer.
I would use an object to keep track of any duplicates.
Also loop from the end, mostly because it feels right, not messing up the index.
Change position in a if current < previous
Else add to the object.
Check if object has previous index, or
Check if current is the same as previous (ex. 8, 8 at the end of the array)
then splice the previous index.
Added numberOfLoops just because it's fun to know.
var a = [2, 2, -1, 8, 5, 1, 3, 3, 13, 2, 8, 8];
var temp = {}; // 1
var result = [];
var current, previous;
var numberOfLoops = 0;
for (let i = a.length - 1; i > 0 ; i--) { // 2
current = a[i];
previous = a[i - 1];
numberOfLoops++; // 8
if (temp.hasOwnProperty(previous) || // 5
current == previous) { // 6
a.splice(i - 1, 1); // 7
// i++;
} else if (current < previous) { // 3
a[i - 1] = current;
a[i] = previous;
i += 2;
delete temp[current];
} else { // 4
temp[current] = current;
}
}
console.log({numberOfLoops}); // 23
console.log(a); // [-1, 1, 2, 3, 5, 8, 13]

Find int that appears an odd number of times in an array

I am working on this task where I need to find a number that happens to appear an odd number of times in an array.
I believe I've almost done it, but if some number appears more than once in a row (like 1 in [1,1,3,1,1]), it will always return that number, no matter if it appears an odd number of times or not.
function findOdd(A) {
var a;
var count = 0;
for (var i = 0; i < A.length; i++) {
a = A[i];
for (var l = i + 1; l < A.length; l++) {
if (a == A[l]) {
count++;
}
}
if (!(count % 2)) {
break;
} else {
count = 0;
}
}
return a;
}
console.log(findOdd([ 1, 1, 2, -2, 5, 2, 4, 4, -1, -2, 5 ]));
I've tried to play with adding 1 to count if [i] = [i+1], but it didn't work.
I'd expect output of findOdd([1, 1, 2, -2, 5, 2, 4, 4, -1, -2, 5]) to be -1, but it is 1. The function always returns first number that happens to be equal to next element of an array.
There's no need to reset count or use a break.
function findOdd(A) {
for (var i = 0; i < A.length; i++){
var count = 0;
for (var l = 0; l < A.length; l++) {
if (A[i] === A[l]) count++;
}
if (count % 2 !== 0) return A[i];
}
}
An important thing to note is that the inner loop is not starting at i+1, its starting at the 0. When A[i] matches A[l], we increment count. A number that appears an odd number of times will result in count becoming odd as well and we can return that number.
The following works but I wonder how the performance compares to simply doing for loops. The complexity seems to be the same.
function findOdd(a) {
let m = {};
a.forEach(e => (m[e] in m) ? m[e] += 1 : m[e] = 1);
for (k in m) {
if (m[k] % 2 != 0) return k;
}
}
console.log(findOdd([1, 1, 3, 1, 1]));
console.log(findOdd([1, 1, 2, -2, 5, 2, 4, 4, -1, -2, 5]));
You could count all values first and then get the value with an odd count.
function getOddCount(array) {
var value,
count = {},
k;
for (value of array) count[value] = (count[value] || 0) + 1;
for (k in count) if (count[k] % 2) return +k;
}
console.log(getOddCount([1, 1, 3, 1, 1]));
console.log(getOddCount([1, 1, 2, -2, 5, 2, 4, 4, -1, -2, 5]));
A naive implementation would simply use an object to store the frequency of each element and then iterate over it at the end to find the element that appeared an odd amount of times.
function findOdd(arr) {
const freq = {};
for(const num of arr){
freq[num] = (freq[num] || 0) + 1;
}
return +Object.keys(freq).find(num => freq[num] % 2 == 1);
}
A more efficient implementation could leverage the properties of the bitwise XOR (^), namely the fact that a ^ a == 0 and that the operation is commutative and associative, leading to the solution of applying XOR on each element of the array to obtain the answer.
function findOdd(arr) {
return arr.reduce((a,c)=>a ^ c, 0);
}

Filling array with averages

Im wondering about a problem I have in Javascript. I have a scenario where I need to fill gaps in an array with averages of the surrounding values. Let me give an example:
Array:
1, 2, 3, ,4, 5
In this particular case I would need to fill the gap with average of the surrounding numbers, i.e. 3.5. I think this is relatively easy to do.
However, I also need to make sure this works when there is more subsequent gaps in the array.
Example:
1, 2, 3, , , 5, 6
In this case the two gaps should be filled with the average of 3 and 5, resulting in ... 3, 4, 4, 5.
The moment I got stuck was when I tried iterating the array and filling the gaps because I filled the first gap with 4, but at that moment, the surrounding numbers for the second gap were 4 (the original gap) and 5, so I ended up with
... 3, 4, 4.5, 5, ...
which is not what I need.
Im using jQuery to iterate the array and get the values from a table.
This is how Im loading the array:
var list = [];
$('#mytable > tbody > tr').each(function() {
$this = $(this);
list.push(eval($this.find("input.number").val());
}
Now I need to iterate and fill the gaps in "list"
Here's one possible implementation:
function fill(arr) {
while (arr.includes(undefined)) {
const startIndex = arr.findIndex(num => num === undefined);
const endIndex = arr.findIndex((num, i) => i >= startIndex && num !== undefined);
const avg = (arr[startIndex - 1] + arr[endIndex]) / 2;
for (let i = startIndex; i < endIndex; i++) arr[i] = avg;
}
return arr;
}
console.log(fill([1, 2, 3, , , 5, 6]));
console.log(fill([1, , , , , , 6]));
console.log(fill([1, , , , 3, 4, , , 6]));
Try this code
var x = "1, 2, 3, ,4, 5";
var y = "1, 2, 3, , , 5, 6";
var z = "1, , , , , , 6";
var q = "1, , , , 3, 4, , , 6";
function removeSpace(str) {
return str.replace(/ /g, "");
}
function splitString(str) {
return str.split(',');
}
function fill(str) {
var z = removeSpace(str);
var zPrime = splitString(z);
for (var i = 0; i < zPrime.length; i++) {
if (zPrime[i] == "") {
if (i + 1 < zPrime.length && zPrime[i + 1] != "") {
zPrime[i] = (Number(zPrime[i - 1]) + Number(zPrime[i + 1])) / 2;
} else {
var j = i + 1;
while (j < zPrime.length && zPrime[j] == "") j++;
var tp = (j < zPrime.length) ? Number(zPrime[j]) : 0;
var dn = (i - 1 > -1) ? Number(zPrime[i - 1]) : 0;
for (var k = i; k < j; k++) {
zPrime[k] = ((tp + dn) / 2) + '';
}
}
}
}
return zPrime;
}
console.log(fill(x));
console.log(fill(y));
console.log(fill(z));
console.log(fill(q));
You can do something like this using simple iteration.
function fillMissing(arr) {
// array for storing result
var res = [];
// iterate over the array
for (i = 0; i < arr.length; i++) {
// if undefined then call the function to calculate average or set the value
res[i] = arr[i] == undefined ? calculateAverage(arr, i) : arr[i];
}
return res;
}
function calculateAverage(arr1, i) {
// set preve and next value as nearest element
var prev = arr1[i - 1],
next = arr1[i + 1],
j = 1; // variable for iterating
// iterate to find out nearest defined value
// after the element
while (prev == undefined) { prev = arr1[i - ++j]; }
j = 1; // reset for next iteration
// iterate to find out nearest defined value
// before the element
while (next == undefined) { next = arr1[i + ++j]; }
//find average and return
return (prev + next) / 2;
}
console.log(fillMissing([1, 2, 3, , , 5, 6]));
console.log(fillMissing([1, 2, 3, , , 5, 6]));
console.log(fillMissing([1, , , , , , 6]));
console.log(fillMissing([1, , , , 3, 4, , , 6]));
You could iterate the sparse array and store the last index and value. If a gap is found, the gap is filled with the average of the last value and the actual value.
function fill(array) {
array.reduce((last, v, i, a) => {
var j, avg;
if (last.index + 1 !== i) {
avg = (v + last.value) / 2;
for (j = 1; j < i - last.index; j++) {
a[last.index + j] = avg;
}
}
return { index: i, value: v };
}, { index: -1 });
return array;
}
console.log(fill([1, 2, 3, , , 5, 6]));
console.log(fill([1, , , , , , 6]));
console.log(fill([1, , , , 3, 4, , , 6]));
Interesting question that made me think a bit.
My first idea was to use map but I found that it does not consider holes. Fixed that with Array.from that converts holes to undefined
I was hoping to find a more concise solution but I'm posting it anyways
function fillMissing(a){
return Array.from(a).map((e,i)=> e !== undefined ? e : averageAround(i,a))
}
const averageAround = (index, array) => average(before(index, array), after(index, array))
const average = (a, b) => (a+b)/2
const before = findFirstNotUndefined.bind(null, -1)
const after = findFirstNotUndefined.bind(null, 1)
function findFirstNotUndefined(direction, index, array){
if (array[index] !== undefined) return array[index]
return findFirstNotUndefined(direction, index+direction, array)
}
console.log(fillMissing([1, 2, 3, , , 5, 6]));
Some thoughts:
recursive call to findFirstNotUndefined is in tail call position
findFirstNotUndefined should be memoizable, (maybe) useful for large holes
average around can be written point free style but not without adding another function or importing some fp library like ramda
Modifying the signature of averageAround like (_, index, array), it can be used directly in the map: Array.from(a).map(averageAround). Cool to read even if it computes the average on every value ((val + val)/2)
There is a recursive way to do this, Basically it counts the empty spots between two values and divides the difference over these empty spots:
var arr = [1, , , , 3, 4, , , 6];
for( var i = 0; i < arr.length; i++ ){
if( arr[ i ] === undefined )
arr[ i ] = avg( arr[ i - 1 ], arr.slice( i + 1, arr.length ), 1 );
}
function avg( low, arr, recursion ){
if( arr[ 0 ] === undefined )
return avg( low, arr.slice( 1, arr.length ), recursion + 1 );
return low + ( ( arr[0] - low ) / ( recursion + 1 ) );
}
console.log( arr );
Works like a charm:
arr = [1, 2, 3, ,4, 5] => [1, 2, 3, 3.5, 4, 5]
arr = [1, 2, 3, , , 5, 6] => [1, 2, 3, 3.6665, 4.3333, 5, 6]
arr = [1, , , , 3, 4, , , 6] => [1, 1.5, 2, 2.5, 3, 4, 4.6667, 5.3334, 6]
Another functional recursive version
const arr = [1,2,3,,,5,6]
function fill([head,...tail], lastNotNull, holeWidth = 0){
if (tail.length === 0 ) return head
if (head === undefined) return fill(tail, lastNotNull, ++holeWidth)
return Array(holeWidth).fill((lastNotNull+head)/2).concat(head).concat(fill(tail, head))
}
console.log(fill(arr))
Explanation:
First parameter of fill function is destructured into head and tail. Head represent the current value and tail next values
Base case: tail empty -> we just return head
If current value head is undefined, we recurse increasing the size of the hole and keeping the last not null value, to compute the average
In all other cases we fill the hole with the average, we add the current value and then recurse onto remaining array, resetting lastNotNull to head and holeWidth to 0 with default value
I feel that the last step could be optimized in terms of execution time but I like the fact that it's compact and clear (if already comfortable with recursion)
Bonus: I love the fact that the hole is filled with the Array.prototype.fill function

Javascript: How to find first duplicate value and return its index?

I have to find first duplicate value in array and then return its index in variable firstIndex. This has to be done with for loop which should stop after having found first duplicate. I know this is probably pretty simple but I got stuck. So far I have this but it doesn't seem to be working:
var numbers4 = [5, 2, 3, 4, 2, 6, 7, 1, 2, 3];
var firstIndex = "";
for (var a = 0; a < numbers4.length; a++) {
for (var b = a+1; b < numbers4.length; b++) {
if (numbers4[a] === numbers4[b])
firstIndex = numbers4.indexOf(numbers4[a]);
break;
}
}
console.log(firstIndex);
Console prints out 1 which is fine because 2 is first duplicate, but when I change numbers in array, the loop doesn't work. Can you advise what can be changed here?
Thanks in advance!
If I correctly understand your question, that's should help you...
Basically, you need for a double iteration.
const firstDupeIndex = list => list.findIndex(
(item, index) => list.lastIndexOf(item) !== index
);
console.log(
"First Dupe at index:",
firstDupeIndex([5, 2, 3, 4, 4, 6, 7, 1, 2, 3])
);
Thee above implementation comes with the drawback of being O(n2), due to nesting the lastIndexOf within the findIndex function.
A better solution would be to index your occurrences by building a dictionary, therefore keeping time complexity to just O(n) in the worst case. Probably a little bit less neat, but surely more efficient with big inputs.
const firstDupeIndex = (list) => {
const dict = {};
for (const [index, value] of list.entries()) {
if (dict.hasOwnProperty(value)) {
return dict[value];
}
dict[value] = index;
}
return -1;
};
console.log(
"First Dupe at index:",
firstDupeIndex(['a', 'b', 'c', 'd', 'e', 'b', 'z', 't', 'c'])
);
Change your code with the following
var numbers4 = [5, 2, 3, 4, 2, 6, 7, 1, 2, 3];
var firstIndex = "";
var isMatch=false;
for (var a = 0; a < numbers4.length; a++) {
for (var b = a+1; b < numbers4.length; b++) {
if (numbers4[a] === numbers4[b]){
firstIndex = numbers4.indexOf(numbers4[a]);
isMatch=true;
break;
}
}
if (isMatch) {break;}
}
console.log(firstIndex);
I would use an object remembering the values already found... Something like that should work ;)
var numbers4 = [5, 2, 3, 4, 4, 6, 7, 1, 2, 3];
function findFirstDuplicateIndex(arr){
var found = {};
for (var a = 0, aa = arr.length; a < aa ; a++) {
if (found[arr[a]])
return found[arr[a]];
found[numbers4[a]] = a
}
}
console.log(findFirstDuplicateIndex(numbers4));
It's quite fast because you just loop one time through the array. The rest of the time you just access an object property or you set the object property... Let me know if you have questions ;)
However maybe there something faster... It's just an idea ^^
PS: It also works with words, not just numbers
Your break; terminates b loop, because if() is not using parenthesis {}.
Additionally, firstIndex should be simply set to either a or b depending on whether you need to return the duplicate's first occurance or first repetition.
It should be:
if (numbers4[a] === numbers4[b])
{
firstIndex = a;
break;
}
Your problem is - you have two loops and only one break, you need to break out of both.
Why not simply return the index as soon as values match?
var numbers4 = [5, 2, 3, 4, 2, 6, 7, 1, 2, 3];
function getDuplicate(numbers4)
{
for (var a = 0; a < numbers4.length; a++) {
for (var b = a+1; b < numbers4.length; b++) {
if (numbers4[a] === numbers4[b]){
return a;
}
}
}
}
console.log(getDuplicate(numbers4 ));
However, you can optimize your code further by keeping a map
function getDuplicate( numbers )
{
var map = {};
for (var a = 0; a < numbers.length; a++)
{
if( map[ numbers[ a ] ] )
{
return a;
}
map[ numbers[ a ] ] = true;
}
return -1;
}
You can check if indexOf() is not equal to lastIndexOf() and return value and break loop
var numbers4 = [5, 2, 3, 4, 2, 6, 7, 1, 2, 3];
var firstIndex = "";
for (var i = 0; i < numbers4.length; i++) {
if (numbers4.indexOf(numbers4[i]) != numbers4.lastIndexOf(numbers4[i])) {
firstIndex = i;
break;
}
}
console.log(firstIndex)
You don't even need nested for loops.
var numbers4 = [5, 2, 3, 4, 2, 6, 7, 1, 2, 3];
var firstIndex = "";
for (var a = 1; a < numbers4.length; a++) { //start from second elem since first is never a duplicate
if (numbers4.lastIndexOf(numbers4[a])> a) {
firstIndex = a;
break;
}
}
console.log(firstIndex); //1
All you have to do during iterating is to check if current value exists somewhere further in array. That is done by checking last index of this value's occurrence using lastIndexOf().
var numbers = [5, 2, 3, 4, 2, 6, 7, 1, 2, 3];
var firstIndex;
var len = numbers.length;
for (var i = 0; i < len; i++) {
var tmpArray = numbers.slice(i + 1, len);
var index = tmpArray.indexOf(numbers[i]);
if (index !== -1) {
firstIndex = index + i + 1;
break;
}
}
console.log(firstIndex);
Update:
Actually your logic is right, but you missed braces for if condition and also if the condition satisfies then it means firstIndex is the same as a
This is your code with braces,
var numbers4 = [5, 2, 3, 4, 2, 6, 7, 1, 2, 3];
var firstIndex = "";
for (var a = 0; a < numbers4.length; a++) {
for (var b = a + 1; b < numbers4.length; b++) {
if (numbers4[a] === numbers4[b]) {
firstIndex = a
break;
}
}
}
console.log(firstIndex);
The question states the first dupe in the array has to be found along with it's index. So I return an object where the i property is the index and the e property is the first duplicate element itself. One way of performing this task would be
var numbers4 = [5, 2, 3, 4, 2, 6, 7, 1, 2, 3],
headDupe = (a,r={}) => (r.e = a.find((n,i) => (r.i = a.indexOf(n), r.i < i)),r);
console.log(headDupe(numbers4));

Finding range in an array

Lets assume we have an array of those elements (always sorted).
[1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]
Our goal is to find the min-index and max-index of a given value e.g. Lets assume we're searching for the min-index and max-index of the element 3.
We quickly see, that the min-index for 3 is 8 and the max-index is 11.
For the value 1, the min is 0 and the max is 3.
How would you develop a solution for that returns the min and max in JavaScript? I have tried to do this, but I can't figure how to, I always get the wrong answer.
You can try Array.indexOf() and Array.lastIndexOf()
var sortedArr =[1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3];
console.log(sortedArr.indexOf(1));
console.log(sortedArr.lastIndexOf(1));
Basically Array.indexOf() and Array.lastIndexOf() will do it but they'll do a linear search (go through whole array with a loop until the element is found) which is obviously done in linear time O(n).
If it is true that your array is always sorted then we can use this property to optimize it and use binary search. It is way faster and will do it in logarithmic time O(log n).
After that we simply check the elements before (and after) the found index and until we find an element which isn't equal to our element.
For finding last occurence:
var i= foundIndex;
while(sortedArr[i] == sortedArr[foundIndex]){
i++;
}
foundIndex = i;
And for first occurence:
var i= foundIndex;
while(sortedArr[i] == sortedArr[foundIndex]){
i--;
}
foundIndex = i;
That's it! This will help a lot with the run time especially if you have big arrays.
You can find binary search implementations everywhere, just use any of them.
Is that what you want?
var myarr = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3];
var minNum = myarr[0];
var maxNum = myarr[1];
var minNumStartINDX, maxNumStartINDX, minNumEndINDX, maxNumEndINDX;
/********************************************/
for (var x = 0; x < myarr.length; ++x) {
if (minNum >= myarr[x]) {
minNum = myarr[x];
minNumEndINDX = x;
}
}
for (var x = 0; x < myarr.length; ++x) {
if (minNum >= myarr[x]) {
minNumStartINDX = x;
break;
}
}
for (var x = 0; x < myarr.length; ++x) {
if (maxNum <= myarr[x]) {
maxNum = myarr[x];
maxNumEndINDX = x;
}
}
for (var x = 0; x < myarr.length; ++x) {
if (maxNum <= myarr[x]) {
maxNumStartINDX = x;
break;
}
}
/********************************************/
console.log(minNum);
console.log(minNumStartINDX + "-" + minNumEndINDX);
console.log(maxNum);
console.log(maxNumStartINDX + "-" + maxNumEndINDX);
var data={1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3};
var value=3;
var min=data.length;
var max=0;
for(var key in data){
if(data[key]==value){
if(key<min){
min=key;
}
if(key > max){
max=key;
}
}
console.log(min);
console.log(max);

Categories

Resources