differntiate same values in array, js - javascript

What i want to achieve is to make every element in array different and unique from others.
So if you imagine such a array:
let myArray = Array(10, 10, 10, 10, 9);
I want to put it through such algorithm:
Takie first element of an array (10) (index = 0).
check if there is element in array with the same value. yes there is - with index 1. if not, check next element...
change myArray[0] to myArray[0] + 1
change myArray[1] to myArray[1] - 1
return myArray
Now array looks like this:
myArray = Array(11, 9, 10, 10, 9)
Now it runs again, and next steps return such array:
myArray = Array(11, 10, 10, 10, 8)
myArray = Array(11, 11, 9, 10, 8)
myArray = Array(12, 10, 9, 10, 8)
myArray = Array(12, 11, 9, 8, 8)
myArray = Array(12, 11, 10, 9, 7)
Now, because there are only unique values, it finishes.
It is easy to make it with while and for loops, but how can i do it with ES6 functional programming?
K.

You could use a nested iteration with short circuit, if two same values are found.
function fn(array) {
return array.some((v, i, a) => a.slice(i + 1).some((w, j) => {
if (v === w) {
++a[i];
--a[i + j + 1];
return true;
}
}));
}
var array = [10, 10, 10, 10, 9];
console.log(array.join(' '));
while (fn(array)) console.log(array.join(' '));
.as-console-wrapper { max-height: 100% !important; top: 0; }
A more functional style, which returns a new array
const
init = j => (v, i, a) => j !== -1
? i === j ? v - 1 : v
: (j = a.indexOf(v, i + 1)) !== -1 ? v + 1 : v,
upDown = () => init(-1);
var array = [10, 10, 10, 10, 9];
do {
console.log(array.join(' '))
} while (array.join() !== (array = array.map(upDown())).join())
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Comparison logic less than but more than when initial value is indeterminate

The code below is a sliver of a larger project involving moment.js, but moment isn't necessary. I'm working in UTC dates and I can use a simple value comparison. I'm looking to return the index that value is after but is the lowest of all values (lower than uc). In other words, lowest value that is more than faketimenow. Since these values are out of order, they all need to be checked against the lowest value.
The code below works, but I'm curious if this can be built without a placeholder uc variable to store an initial starting variable. This would be easy if I was looking for greatest, but not certain how to compare against a value that has an indeterminate initial value. I tried using arr[0] as the starting value but that is passed through since its unluckily the least number.
// random range for demonstration purposes
var arr = [1, 2, 3, 4, 100, 17, 22, 15, 13, 11, 23];
var faketimenow = 10;
//initial index to use
var indextouse = 0;
// using a variable with initial start value that is ridiculously out of scope
var uc = 1000000000000;
for (i = 0; i < arr.length; i++) {
console.log("i: " + i + " , faketimenow: " + faketimenow + " , indextouse: " + indextouse + ", uc: " + uc);
if (arr[i] > faketimenow && arr[i] < uc) {
uc = arr[i];
indextouse = i;
}
}
console.log(arr[indextouse]);
You could take a single loop approach without sorting and return the index of the smallest value above the given value.
var array = [1, 2, 3, 4, 100, 17, 22, 15, 13, 11, 23],
value = 10,
index = -1,
i;
for (i = 0; i < array.length; i++) {
if (array[i] > value && (index === -1 || array[i] < array[index])) index = i;
}
console.log(array[index]);
With reduce.
var array = [1, 2, 3, 4, 100, 17, 22, 15, 13, 11, 23],
value = 10,
index = array.reduce((r, v, i, a) => v > value && (r === -1 || v < a[r]) ? i : r, -1);
console.log(array[index]);
You can use Infinity as the initial value, everything is lower than that.
But a simpler solution is to sort the array and then find the first element higher than faketimenow.
// random range for demonstration purposes
var arr = [1, 2, 3, 4, 100, 17, 22, 15, 13, 11, 23];
var faketimenow = 10;
arr.sort((a, b) => a - b);
var uc = arr.find(n => n > faketimenow);
console.log(uc);

Group array elements into set of n

I have an array
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
I want to group it into a set of n arrays such that first n elements in result[0] next n elements in result[1] and if any element is remaining it is discarded.
let sampleOutput = [[0, 1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13]] for n = 7;
Here is my code:
function group5(arr, len) {
let result = [];
let loop=parseInt(arr.length/len)
for (let i=0; i<arr.length; i+=len) {
let x = []; let limitReached = false;
for (let j=0; j<len; j++) {
if (arr[i+j]) {
x.push(arr[i+j]);
} else {
limitReached = true;
break;
}
}
if (!limitReached) {
result.push(x);
} else {
break;
}
}
return result;
}
But I am unable to get expected result. I have tried following things.
Map function
Running i loop to arr.len
Checking arr.len % 7
Creating an array for every third element.
This question is not duplicate of Split array into chunks because I have to discard extra elements that can not be grouped into sets of n.
I have to keep the original array Immutable because I am using this on props in a child component. I need a function that does not modify the original array.
It's pretty straigthforward using Array.from
const list = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14];
function chunkMaxLength(arr, chunkSize, maxLength) {
return Array.from({length: maxLength}, () => arr.splice(0,chunkSize));
}
console.log(chunkMaxLength(list, 7, 2));
What about :
function group5(arr, len) {
let chunks = [];
let copy = arr.splice(); // Use a copy to not modifiy the original array
while(copy.length > len) {
chunks.push(copy.splice(0, len));
}
return chunks;
}
You could use a combination of reduce and filter to achieve the expected result. This example gives you a third control over length which makes the code a bit more reuseable.
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
const groupNumber = 7;
const groupCount = 2;
const groupArray = (group, size, length) => group.reduce((accumulator, current, index, original) =>
((index % size) == 0)
? accumulator.concat([original.slice(index, index + size)])
: accumulator, []
).filter((single, index) => index < length)
const test = groupArray(arr, groupNumber, groupCount);
console.log(test);
Step by Step
const groupArray = (group, size, length) => {
// if (index modulus size) equals 0 then concat a group of
// length 'size' as a new entry to the accumulator array and
// return it, else return the accumulator
const reducerFunc = (accumulator, current, index, original) =>
((index % size) == 0)
? accumulator.concat([original.slice(index, index + size)])
: accumulator
// if the current index is greater than the supplied length filter it out
const filterFunc = (single, index) => index < length;
// reduce and filter original group
const result = group.reduce(reducerFunc, []).filter(filterFunc)
return result;
}
Also (apart from the existing approaches) you can have a recursive approach like this
function chunks(a, size, r = [], i = 0) {
let e = i + size;
return e <= a.length ? chunks(a, size, [...r, a.slice(i, e)], e) : r;
}
function chunks(a, size, r = [], i = 0) {
let e = i + size;
return e <= a.length ? chunks(a, size, [...r, a.slice(i, e)], e) : r;
}
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
console.log('Chunk with 3: ', chunks(arr, 3));
console.log('Chunk with 4: ', chunks(arr, 4));
console.log('Chunk with 5: ', chunks(arr, 5));
console.log('Chunk with 6: ', chunks(arr, 6));
console.log('Chunk with 7: ', chunks(arr, 7));
I able to solve the problem with this code
function groupN(n, arr) {
const res = [];
let limit = 0;
while (limit+n <= arr.length) {
res.push(arr.slice(limit, n + limit));
limit += n
}
return res
}
I usually prefer declarative solutions (map, reduce, etc), but in this case I think a for is more understandable:
function groupArray(array, num) {
const group = [];
for (let i = 0; i < array.length; i += num) {
group.push(array.slice(i, i + num));
}
return group;
}

Grouping an array elements in Javascript

How can I group the following array if the subtraction of elements is less than 2?
var myarr = [1.7, 2, 1.4, 6, 7, 14, 15, 21,31,33.2,33.5]
And I want to have this result:
var myarr = [[1.7, 2, 1.4], [6, 7], [14, 15], [21],[31,33.2,33.5]]
Reduce the array, and add a new sub array to the accumulator if the delta between the current and previous numbers is greater or equal to 2 (or it's the 1st number). Push the current number to the last array:
var myarr = [1.7, 2, 1.4, 6, 7, 14, 15, 21,31,33.2,33.5]
var result = myarr.reduce(function(r, n, i, arr) {
if(i === 0 || Math.abs(n - arr[i - 1]) >= 2) r.push([])
r[r.length - 1].push(n)
return r
}, []);
console.log(result)
I suggest to use the absolute value of the difference for checking with the wanted delta for inserting a new array in the result set, because the order of the values is not strictly ascending.
var array = [1.7, 2, 1.4, 6, 7, 14, 15, 21, 31, 33.2, 33.5],
delta = 2,
grouped = array.reduce(function (r, v, i, a) {
if (!i || Math.abs(a[i - 1] - v) > delta) {
r.push([]);
}
r[r.length -1].push(v);
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Generate valid combinations of numbers in array of digits

I’m trying to generate all valid combinations of numbers from an array of digits. Let’s assume we have the following:
let arr = [1, 2, 9, 4, 7];
We need to output something like this:
1 2 9 4 7
1 2 9 47
1 2 94 7
1 2 947
1 29 4 7
1 29 47
1 294 7
1 2947
12 9 4 7
12 9 47
12 94 7
12 947
129 4 7
129 47
1294 7
12947
An invalid number would be 91, 497, 72 and so on.
I tried this but I’m not satisfied with the result:
const combination = (arr) => {
let i, j, temp;
let result = [];
let arrLen = arr.length;
let power = Math.pow;
let combinations = power(2, arrLen);
for (i = 0; i < combinations; i += 1) {
temp = '';
for (j = 0; j < arrLen; j++) {
if ((i & power(2, j))) {
temp += arr[j];
}
}
result.push(temp);
}
return result;
}
const result = combination([1, 2, 9, 4, 7]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Any ideas?
This code does what you want:
const arr = [1, 2, 9, 4, 7],
result = Array.from({length: 2 ** (arr.length - 1)}, (_, index) => index.toString(2).padStart(arr.length - 1, "0"))
.map((binary) => JSON.parse("[" + arr.map((num, position) => num + (Number(binary[position]) ? "," : "")).join("") + "]"));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It results in:
[
[12947],
[1294, 7],
[129, 47],
[129, 4, 7],
[12, 947],
[12, 94, 7],
[12, 9, 47],
[12, 9, 4, 7],
[1, 2947],
[1, 294, 7],
[1, 29, 47],
[1, 29, 4, 7],
[1, 2, 947],
[1, 2, 94, 7],
[1, 2, 9, 47],
[1, 2, 9, 4, 7]
]
Assuming, the expected result does not depend on order, the spaces represent a binary pattern:
12947 => 0000
1294 7 => 0001
129 47 => 0010
…
1 29 47 => 1010
…
1 2 9 4 7 => 1111
We can utilize this pattern with a counter that we convert to a binary string. We also pad that string with 0 so it always remains 4 digits long:
index.toString(2).padStart(arr.length - 1, "0")
For n digits in arr, there are exactly 2n - 1 combinations, so we use:
{length: 2 ** (arr.length - 1)}
This is an object that has a length property of 2arr.length - 1.
We combine both those things into an Array.from call which accepts two arguments:
an object to turn into an array
a function for mapping each slot
Turning an object with a length property into an array means that we create an array with length many slots.
The mapping function accepts the index of a slot as the second parameter. We only use the index — as a counter for our binary number.
So, finally this whole expression:
Array.from({length: 2 ** (arr.length - 1)}, (_, index) => index.toString(2).padStart(arr.length - 1, "0"))
evaluates to the following array:
[
"0000",
"0001",
"0010",
"0011",
"0100",
"0101",
"0110",
"0111",
"1000",
"1001",
"1010",
"1011",
"1100",
"1101",
"1110",
"1111"
]
We need to further map this to the final result:
.map((binary) => …)
For each array element, binary is one of the binary strings from the array above.
In order to turn e.g. "0110" into something like "12,9,47", we need to map over arr as well. Every digit num from arr should be followed by , at position, iff binary is 1 at position:
arr.map((num, position) => num + (Number(binary[position]) ? "," : "")).join("")
The expression (Number(binary[position]) ? "," : "") evaluates binary at the specified position as a number. If it’s truthy, i.e. anything but 0, it evaluates to ",", if it’s falsy, i.e. 0, it evaluates to "".
So an intermediate array would look like ["1", "2,", "9,", "4", "7"]. All of this is joined together to "12,9,47".
Then, with JSON.parse("[" + … + "]") it’s being treated and parsed as an array, so it turns into [12, 9, 47]. Since these steps are applied for each binary string, you’ll end up with the final result.
2 ** (arr.length - 1) can be replaced by Math.pow(2, arr.length - 1) if ECMAScript 7 is not supported.
{length: 2 ** (arr.length - 1)} can be replaced by new Array(2 ** (arr.length - 1)).
(Number(binary[position]) ? "," : "") can be replaced by ["", ","][Number(binary[position])]. In this case the evaluated number will be used as an index for a temporary array.
So you need to iterate over all the combinations of "space" and "not space" between all the numbers. With n items, there will be n - 1 spaces, and 2 ** (n - 1) different lists.
So you could do something like this to get all the possible lists:
const combination = arr => {
const len = arr.length;
const n = Math.pow(2, len - 1);
const combinations = [];
for (let i = 0; i < n; i++) {
let this_combination = [arr[0]];
for (let j = 1; j < len; j++) {
if (i & Math.pow(2, j - 1)) {
// If the jth bit is on, no space. Append to the last element.
const last_index = this_combination.length - 1;
this_combination[last_index] = +(this_combination[last_index] + '' + arr[j]);
} else {
// Otherwise create a new list item.
this_combination.push(arr[j]);
}
}
// Consider making this function a generator and making this a yield.
combinations.push(this_combination);
}
return combinations;
}
const result = combination([1, 2, 9, 4, 7]);
console.log(result.map(line => line.join(' ')).join('\n'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you wanted each individual item seperately, for each item in the array, combine it with no other item, then just the next item, then the next 2 items, etc. untill the end:
const combination = arr => {
const len = arr.length;
const combinations = [];
for (let i = 0; i < len; i++) {
let item = arr[i];
combinations.push(item);
for (let j = i + 1; j < len; j++) {
item = +(item + '' + arr[j]);
combinations.push(item);
}
}
return combinations;
}
const result = combination([1, 2, 9, 4, 7]);
console.log(result.join('\n'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could take a recursive approach by iterating the array and insert a space or not and fork the calling of the same function with an incremented index.
function combine(array) {
function fork(i, p) {
if (i === array.length) {
result.push(p);
return;
}
fork(i + 1, p + ' ' + array[i]);
fork(i + 1, p + array[i]);
}
var result = [];
fork(1, array[0].toString());
return result;
}
console.log(combine([1, 2, 9, 4, 7]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could do this by using below code where 3 pointer is used,
1st pointer print 0th position to cursor position.
2nd pointer print cursor to diffidence position in each iteration .
3rd pointer print cursor position to last position.
let arr = [1, 2, 9, 4, 7];
console.log(arr.join(','));
for(let diff=2;diff<=arr.length;diff++){
for(i=0,j=diff;arr.length>=i+diff;j++,i++){
var temp = [];
if(i>0)
temp.push(arr.slice(0,i).join(','));
temp.push(arr.slice(i,j).join(''));
if(j<arr.length)
temp.push(arr.slice(j,arr.length).join(','));
console.log(temp.join(','));
}
}

Array truncation with splice method

I need to delete occurrences of an element if it occurs more than n times.
For example, there is this array:
[20,37,20,21]
And the output should be:
[20,37,21]
I thought one way of solving this could be with the splice method
First I sort the array it order to make it like this:
[20,20,37,21]
Then I check if the current element is not equal to the next and split the array into chunks, so it should look like:
[20, 20],[37],[21]
Later I can edit the chunk longer than 1 and join it all again.
This is what the code looks like in my head but didn't work in real life
var array = [20, 37, 20, 21];
var chunk = [];
for(i = 0; i < array.length; i++) {
if(array[i] !== array[i + 1]) {
var index = array.indexOf(array[i]);
chunk.push = array.splice(0, index) // cut from zero to last duplicate element
} else
var index2 = a.indexOf(a[i]);
chunk.push(a.splice(0, index));
}
with this code the output is
[[], [20, 20]]
I think It's something in the 'else' but can't figure it out what to fix.
As the logic you want to achieve is to delete n occurrences of element in an array, your code could be as follow:
var array = [1, 1, 3, 3, 7, 2, 2, 2, 2];
var n = 2;
var removeMultipleOccurences = function(array, n) {
var filteredArray = [];
var counts = {};
for(var i = 0; i < array.length; i++) {
var x = array[i];
counts[x] = counts[x] ? counts[x] + 1 : 1;
if (counts[x] <= n) filteredArray.push(array[i])
}
return filteredArray;
}
console.log(removeMultipleOccurences(array, n));
I came up with this one, based on array filter checking repeated values up to a limit, but I can see #Basim's function does the same.
function removeDuplicatesAbove(arr, max) {
if (max > arr.length) {max = arr.length;}
if (!max) {return arr;}
return arr.filter(function (v, i) {
var under = true, base = -1;
for (var n = 0; n < max; n++) {
base = arr.indexOf(v, base+1); if (base == -1) {break;}
}
if (base != -1 && base < i) {under = false;}
return under;
});
}
var exampleArray = [20, 37, 20, 20, 20, 37, 22, 37, 20, 21, 37];
console.log(removeDuplicatesAbove(exampleArray, 3)); // [20, 37, 20, 20, 37, 22, 37, 21]
Always when you use splice() you truncate the array. Truncate the array with the length of same values from the start with the help of lastIndexOf(). It always starts from 0.
[ 1, 1, 1, 2, 2, 2, 3, 4, 4, 5 ] // splice(0, 3)
[ 2, 2, 2, 3, 4, 4, 5 ] // splice(0, 3)
[ 3, 4, 4, 5 ] // splice(0, 1)
[ 4, 4, 5 ] // splice(0, 2)
[ 5 ] // splice(0, 1)
Do this as long as the array length is greater than 0.
var arr = [1, 1, 1, 2, 2, 2, 3, 4, 4, 5];
var res = [];
while (arr.length > 0) {
var n = arr[0];
var last = arr.lastIndexOf(n) + 1;
res.push(n);
arr.splice(0, last);
}
console.log(res);
You can use Array.prototype.reduce(), Array.prototype.filter() to check if n previous elements are the same as current element
let cull = (arr, n) => arr.reduce((res, curr) => [...res
, res.filter(v => v === curr).length === n
? !1 : curr].filter(Boolean), []);
let arrays = [[20,37,20,21], [1,1,3,3,7,2,2,2,2]];
let cullone = cull(arrays[0], 1);
let cullthree = cull(arrays[1], 3);
console.log(cullone // [20, 37, 21]
, cullthree // [1, 1, 3, 3, 7, 2, 2, 2]
);

Categories