The function unique_in_order which takes as argument a sequence and returns a list of items without any elements with the same value next to each other and preserving the original order of elements.
For example:
uniqueInOrder('AAAABBBCCDAABBB') == ['A', 'B', 'C', 'D', 'A', 'B']
uniqueInOrder('ABBCcAD') == ['A', 'B', 'C', 'c', 'A', 'D']
uniqueInOrder([1,2,2,3,3]) == [1,2,3]
What you want to do first is normalize your input such that whether it's a string or an array, it doesn't matter.
const input = Array.isArray(x) ? x : x.split('');
After the above, the input will always be an array. Now the logic for skipping is duplicates is quite simple
for (let i = 0; i < input.length; ++i) {
if (input[i] == input[i + 1]) continue
result.push(input[i])
}
You continue, i.e. skip each index in the input that is the same as the next for each index. Then for each element that isn't duplicated, you push into your result array.
The whole thing looks like this:
function uniqueInOrder(x) {
const result = [];
const input = Array.isArray(x) ? x : x.split('');
for (let i = 0; i < input.length; ++i) {
if (input[i] == input[i + 1]) continue
result.push(input[i])
}
return result
}
console.log(uniqueInOrder('AAAABBBCCDAABBB'));
console.log(uniqueInOrder('ABBCcAD'));
console.log(uniqueInOrder([1, 2, 2, 3, 3]));
If you're familiar with the filter function you can make it a one liner.
function uniqueInOrder(x) {
return (Array.isArray(x) ? x : x.split(''))
.filter((c, i) => c !== x[i + 1]);
}
console.log(uniqueInOrder('AAAABBBCCDAABBB'));
console.log(uniqueInOrder('ABBCcAD'));
console.log(uniqueInOrder([1, 2, 2, 3, 3]));
Offered is two different solutions, depending on what you want:
You can use Set() for unique sets. Simply adding the value to a set will retain the order and prevent unique values from being added.
For contiguously unique sets, you can use filter to iterate over an array.
If the argument is a string, this array is created by splitting on a blank string (.split(''))
When iterating over the array of input values, you can compare the current value to the previous; if they are different then return true
A true response will include the value in the array you will be creating as a result of the filter (which holds your unique values)
uniqueInOrder('AAAABBBCCDAABBB') == ['A', 'B', 'C', 'D', 'A', 'B']
uniqueInOrder('ABBCcAD') == ['A', 'B', 'C', 'c', 'A', 'D']
uniqueInOrder([1, 2, 2, 3, 3]) == [1, 2, 3]
function unique(input) {
var set = new Set(),
arr = input;
if (typeof input == 'string')
arr = arr.split('');
arr.forEach(val => {
set.add(val)
});
console.log('input:',input);
console.log('unique:',[...set]);
return [...set];
}
function uniqueInOrder(input) {
var arr = input;
if (typeof input == 'string')
arr = arr.split('');
var unique = arr.filter((val,i,arr)=>{
return val !== arr[i-1];
});
console.log('input:', JSON.stringify(input));
console.log('uniqueInOrder:', JSON.stringify(unique));
return unique;
}
Related
I have an algorithm where some array will be given a new index, but the new index is not sequential. For the sake of time, just assume the new index is given to the array at random. Let's say I have this array:
const arr = [];
arr[5] = 'a'
arr[2] = 'b'
arr[9] = 'c'
arr[0] = 'd'
If I console.log this array to the output window, I get this:
['d', empty, 'b', empty × 2, 'a', empty × 3, 'c']
I'm wondering if it's possible to close/remove those empty gaps between values so the array entries are sorted by their actual index, so I can get this output:
['d', 'b', 'a', 'c']
Is there a short and easy way to do this?
You can just use Array.prototype.filter and filter out undefined values. You can take a shortcut by coercing them into boolean using the !! operator:
arr.filter(x => !!x);
This check is loose/dirty because anything falsy will be coerced to false, e.g. the numeric 0. If you really want to be strict and only filter out undefined, then you need a stricter predicate:
array.filter(x => typeof x !== 'undefined');
const arr = [];
arr[5] = 'a';
arr[2] = 'b';
arr[9] = 'c';
arr[0] = 'd';
console.log(arr);
console.log(arr.filter(x => !!x));
console.log(arr.filter(x => typeof x !== 'undefined'));
simply use Array.splice() method
to "clean up" your array, wthiut creating a new one.
need to use reverse index progression
const arr = [];
arr[5] = 'a';
arr[2] = 'b';
arr[9] = 'c';
arr[0] = 'd';
console.log(JSON.stringify(arr));
for (let i=arr.length; i-- > 0;)
{
if (arr[i]===undefined) arr.splice(i,1);
}
console.log(JSON.stringify(arr));
Let's say I have two arrays of same length:
const a = ['a', 'b', 'c', 'd'];
const b = [1, 1, 2, 1];
I want to join (.join('')) all consecutive items in a whose corresponding value in b (i.e. at the same index) are equal.
In this scenario, what I want to get is:
const result = ['ab', 'c', 'd']
Because a[0] and a[1] have the same corresponding value in b (i.e. b[0] === b[1]) and are consecutive in a, they are joined into the same string, forming a single item in result. However, although a[3]'s corresponding value in b is equal to a[0] and a[1]'s one, it's not joined to any of the latter as it isn't consecutive.
Just check if the value at b is the same at the given index and the sucessor.
const
a = ['a', 'b', 'c', 'd'];
b = [1, 1, 2, 1],
result = a.reduce((r, v, i) => {
if (b[i - 1] === b[i]) r[r.length - 1] += v;
else r.push(v);
return r;
}, []);
console.log(result);
You can use two seporate loops; one to combine the two arrays in an object and merge consecutive objects with the same id, and one to grab the other value from the first loop and create a new array.
const a = ['a', 'b', 'c', 'd'];
const b = [1, 1, 2, 1];
var step = [];
for (n = 0; n < a.length; n++) {
if(step[n-1] !== undefined && step[n-1].id === b[n]) {
step[step.length-1].text = step[step.length-1].text + a[n];
} else {
step.push({ id: b[n], text: a[n] });
}
}
var result = [];
for (i = 0; i < step.length; i++) {
result.push(step[i].text);
}
console.log(a);
console.log(b);
//console.log(step); //for testing
console.log(result);
I would like to calculate the common segments in arrays that are already sorted:
Consider the following two arrays:
var arr1 = ['a','b','c','d'];
var arr2 = ['a','c','d'];
I would like to return ['a'],['b'],['c','d']
This is not the typical intersection, maintaining the order of the array values is crucial.
Is there a simple way to do this using underscore?
Presumably this works for you, at least with the given data.
var arr1 = ['a', 'b', 'c', 'd'],
arr2 = ['a', 'c', 'd'],
arr3 = [];
arr1.forEach(function (a, i) {
if (!i || this.next) {
arr3.push([a]);
if (a === arr2[this.index]) {
this.index++;
}
this.next = false;
return;
}
if (a < arr2[this.index]) {
arr3.push([a]);
this.next = true;
return;
}
if (a === arr2[this.index]) {
arr3[arr3.length - 1].push(a);
this.index++;
}
}, { index: 0, next: false });
document.write('<pre>' + JSON.stringify(arr3, 0, 4) + '</pre>');
How to I change the object in the array with For-of loop?
Following code:
let arr = ['a', 'b', 'c', 'd', 'e'];
for (let v of arr) {
if (v === 'c') {
v = 'f';
break;
}
}
console.log(arr);
I want to find the first letter c and change it to an f, but arr doesn't get changed, probably because it is not referenced ? But shouldn't the v of arr make that the object v is the same as the one in arr ?
Javascript does not create references to simple values such String. To get array referrenced, you need to let array be an array of objects like [{char : 'a'}, {char : 'b'}, ...]. Then in your iterator you can change elements of array through changing of the char property
let arr = [{char: 'a'}, {char :'b'}, ...];
for (let v of arr) {
if (v.char === 'c') {
v.char = 'f';
break;
}
}
v is not a reference to the array element, it's just a variable that is assigned the value that the array iterator yields. If you assign to it, only the variable v changes but not the array.
To do that, you need to explicitly reference the property with its index and assign to it:
let arr = ['a', 'b', 'c', 'd', 'e'];
for (let [i, v] of arr.entries()) {
if (v === 'c') {
arr[i] = 'f';
break;
}
}
console.log(arr); // ['a', 'b', 'f', 'd', 'e']
An iterator does not provide a way to mutate the underlying structure, you need to do it yourself.
As detailed in the MDN spec, let is declaring a new variable from a copy of your array element. So changing the v will not change the value in the array. This is shared by var and const and is simply just javascripts behaviour. When you create a new object, it starts empty.
Your loop is basically saying "For every element in arr, declare a variable holding a copy of it and then if that variable is c, change that copy to f"
Before you change v both, the array and v point to - or in this case have - the same value:
arr[2] -> 'c' <- v
After you change v it has a different value, but you didn't change the array:
arr[2] -> 'c' v -> 'f'
v and arr[2] are only placeholders, but different ones.
The correct answer:
let arr = ['a', 'b', 'c', 'd', 'e'];
let i = 0;
for (let v of arr) {
if (v == 'c') {
arr[i] = 'f';
break;
}
i++;
}
console.log(arr);
:-P
I have a 1 dimensional array like:
var abc = ['a','a','b','a','c']
Now I want to get back all the indexes of 'a', that is 0, 1 and 3.
Are there any simple solutions?
P.S.
I know IndexOf or jQuery.inArray(). But they just returned the index of first matched element only
You could extend the basic Array Object with the following method:
Array.prototype.multiIndexOf = function (el) {
var idxs = [];
for (var i = this.length - 1; i >= 0; i--) {
if (this[i] === el) {
idxs.unshift(i);
}
}
return idxs;
};
Then the operation
var abc = ['a','a','b','a','c'];
abc.multiIndexOf('a');
would give you the result:
[0, 1, 3]
Jsperf comparison of unshift / push / push(reverse order)
You could use Array#reduce with Array#concat with a check for the wanted item, take the index or an empty array.
var abc = ['a', 'a', 'b', 'a', 'c'],
indices = abc.reduce((r, v, i) => r.concat(v === 'a' ? i : []), []);
console.log(indices);
ES5
var abc = ['a', 'a', 'b', 'a', 'c'],
indices = abc.reduce(function (r, v, i) {
return r.concat(v === 'a' ? i : []);
}, []);
console.log(indices);
Rather than using a for loop, you can use a while loop combined with indexOf:
var array = [1, 2, 3, 4, 2, 8, 5],
value = 2,
i = -1,
indizes = [];
while((i = array.indexOf(value, i + 1)) !== -1) {
indizes.push(i);
}
This will return you [1, 4] and of course could be combined with extending the prototype of Array.
The second argument of indexOf specifies where to start the search in the given array.
You can take advantage of the fact that $.map() does not push values in its resulting array when the function you pass returns undefined.
Therefore, you can write:
var abc = ["a", "a", "b", "a", "c"];
var indices = $.map(abc, function(element, index) {
if (element == "a") {
return index;
}
});
You also use reduce function on the array and push the indexes to accumulated array, you need start with an empty array, the good thing about reduce is it's async and also time execution is faster than for loop, also it's a native function on array, look at the below, hope it's helping:
var arr = [0, 1, 2, 3, 7, 2, 3, 4, 7, 8, 9, 2, 3];
function indexesOf(num) {
var reduced = arr.reduce(function(acc, val, ind, arr){
if(val === num){
acc.push(ind);
}
return acc;
}, []);
return reduced;
}
indexesOf(2); //[2, 5, 11]
AFAIK, there's no Javascript or jQuery function that does this in one step, you have to write a loop.
var indexes = [];
$.each(abc, function(i, val) {
if (val == "a") {
indexes.push(i);
}
}
Do it this way :
var abc = ['a','a','b','a','c'];
for (var i=0; i<abc.length; i++) {if(abc[i]=='a') {console.log(i)};}
If your array size is fixed, then you can find the first occurrence in the array using indexOf(). Use the found index value as starting point in indexOf() to find an other occurrence.
var firstOccurance = [your_array].indexOf(2)
var secondOccurance = [your_array].indexOf(2, firstOccurance + 1)
Demo
use for loop
var arr = ['a', 'a', 'b', 'a', 'c'];
var indexA = [];
for (var i = 0; i < arr.length; i++) {
if ("a" == arr[i]) indexA.push(i)
}
With ES6 syntax you could go with forEach and the ternary operator :
const abc = ['a','a','b','a','c']
let matchingIndexes = []
abc.forEach( (currentItem, index) => {
currentItem === 'a' ? matchingIndexes.push(index) : null
})
console.log(matchingIndexes) // [0, 1, 3]