Conditionally join some of an array's items in JavaScript - javascript

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);

Related

Substitute random items in a Javascript array

In Javascript, am trying to randomly substitute half (in this case, 3 out of 6) of the items from an array with different ones (all of the same type), and I need the original items' position to be kept. So for instance, if I have:
var my_array = [a, b, c, d, e, f]
I would want to select three random ones to be substituted with a 0, while the others to keep their initial position. So let's say a, c, and d are the ones the random selector will make go away on one instance, then my array would become:
my_array = [0, b, 0, 0, e, f]
On a different run, the random selector would perhaps pick b, c, and f and so I'd have:
my_array = [a, 0, 0, d, e, 0]
And so on.
Thank you so much for your help!
You could take a closure over the array and wanted zero counts and return a function which generates random integer and map the array with zeros or values.
function getRandom(array, count) {
return function () {
const indices = new Set();
do {
indices.add(Math.floor(Math.random() * array.length));
} while (indices.size < count)
return array.map((v, i) => indices.has(i) ? 0 : v);
};
}
var myArray = ['a', 'b', 'c', 'd', 'e', 'f'],
getArrayWithZeros = getRandom(myArray, 3);
console.log(...getArrayWithZeros());
console.log(...getArrayWithZeros());
console.log(...getArrayWithZeros());
console.log(...getArrayWithZeros());
console.log(...getArrayWithZeros());
Another option
const myArray = ['a', 'b', 'c', 'd', 'e', 'f'];
// Randomizer function
const rand = ([...arr], len, rep) => {
let ins = {};
while(Object.keys(ins).length < len) {
let r = ~~(Math.random() * arr.length);
if(!ins[r]) ins[r] = true;
}
for(let i = 0; i < arr.length; i++) if(ins[i]) arr[i] = rep;
return arr;
}
// Array, number of elements to replace, replace with
// Here we transform toString for better readability
console.log(rand(myArray, 3, 0).toString());
console.log(rand(myArray, 3, 0).toString());
console.log(rand(myArray, 3, 0).toString());
console.log(rand(myArray, 3, 0).toString());
console.log(rand(myArray, 3, 0).toString());
You need to calculate half of the length of the array, generate random indexes of this amount, and then change them:
var my_array = ['a', 'b', 'c', 'd', 'e', 'f'];
function alterHalfWithZero(arr){
let my_array = [...arr];
let indexList = {};
let len = Math.floor(my_array.length / 2)
while(Object.values(indexList).length != len){
let random_index = Math.floor(Math.random() * my_array.length);
indexList[random_index] = random_index;
}
indexList = Object.values(indexList);
for(let i = 0; i < indexList.length; i++)
my_array[indexList[i]] = 0;
return my_array;
}
console.log(...alterHalfWithZero(my_array));
console.log(...alterHalfWithZero(my_array));
console.log(...alterHalfWithZero(my_array));
First get unique random indexes.
Loop over the indexes and make them 0.
var my_array = ["a", "b", "c", "d", "e", "f"];
// Get unique random indexes
const random = (num, count) => {
const set = new Set();
while (set.size < count) {
set.add(Math.floor(Math.random() * num));
}
return [...set];
};
const alter = (arr, count = 3) => {
const output = [...arr];
random(arr.length, count).forEach((index) => (output[index] = 0));
return output;
};
console.log(alter(my_array));
console.log(alter(my_array));
Here is one solution.
var my_array = ["a", "b", "c", "d", "e", "f"];
const substitute = array => {
const indexes = Array.from(Array(array.length).keys())
.sort(() => Math.random() - 0.5)
.slice(0, array.length / 2);
return array.map((value, index) =>
indexes.some(i => i === index) ? value : 0
);
};
console.log(substitute(my_array));

How to group every 2nd and 3rd items of an array into sub-arrays?

I have an array of objects
const objects = [a, b, c, d, e, f, g ... ]
and I want them to turn into
const result = [a, [b, c], d, [e, f], g ... ]
Any ideas?
[Edit] My apologies. This is my first post, didn't know I have to show my attempts. I don't think I deserve the mean comments either, be nice people. I solved it after a head-banging 4 hours. Here is my solution:
const result = []
const method = array => {
for (let i = 0; i < array.length; i += 3) {
const set = new Set([array[i + 1], array[i + 2]])
if (i !== array.length - 1) {
result.push(array[i])
result.push(Array.from(set))
} else {
result.push(array[i])
}
}
}
Thanks for the responses guys! I read every single one of them.
You could take a while loop and push either an item or a pair of items.
var array = ['a', 'b', 'c', 'd', 'e', 'f', 'g'],
grouped = [],
i = 0;
while (i < array.length) {
grouped.push(array[i++]);
if (i >= array.length) break;
grouped.push(array.slice(i, i += 2));
}
console.log(grouped);
You can do this with plain for loop and % modulo operator.
const objects = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
const result = []
for(let i = 0; i < objects.length; i++) {
if(i % 3 === 0) {
const arr = objects.slice(i + 1, i + 3)
result.push(objects[i])
if(arr.length) result.push(arr)
}
}
console.log(result)
this is my solution:
const objects = ["a", "b", "c", "d", "e", "f", "g"];
let result = [];
let toGroup = false;
for(let i = 0; i < objects.length ; i++){
if(toGroup){
result.push([objects[i], objects[++i]]);
}
else result.push(objects[i]);
toGroup = !toGroup;
}
this has a particular case that you have not specified, where it doesn't work, for example if inside objects there are 2 elements, and so i don't know what you would like to do in that case

Remove all adjacent repeating characters from input

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;
}

segments of common values in sorted arrays

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>');

Finding All Combinations (Cartesian product) of JavaScript array values

How can I produce all of the combinations of the values in N number of JavaScript arrays of variable lengths?
Let's say I have N number of JavaScript arrays, e.g.
var first = ['a', 'b', 'c', 'd'];
var second = ['e'];
var third = ['f', 'g', 'h', 'i', 'j'];
(Three arrays in this example, but its N number of arrays for the problem.)
And I want to output all the combinations of their values, to produce
aef
aeg
aeh
aei
aej
bef
beg
....
dej
EDIT: Here's the version I got working, using ffriend's accepted answer as the basis.
var allArrays = [['a', 'b'], ['c', 'z'], ['d', 'e', 'f']];
function allPossibleCases(arr) {
if (arr.length === 0) {
return [];
}
else if (arr.length ===1){
return arr[0];
}
else {
var result = [];
var allCasesOfRest = allPossibleCases(arr.slice(1)); // recur with the rest of array
for (var c in allCasesOfRest) {
for (var i = 0; i < arr[0].length; i++) {
result.push(arr[0][i] + allCasesOfRest[c]);
}
}
return result;
}
}
var results = allPossibleCases(allArrays);
//outputs ["acd", "bcd", "azd", "bzd", "ace", "bce", "aze", "bze", "acf", "bcf", "azf", "bzf"]
This is not permutations, see permutations definitions from Wikipedia.
But you can achieve this with recursion:
var allArrays = [
['a', 'b'],
['c'],
['d', 'e', 'f']
]
function allPossibleCases(arr) {
if (arr.length == 1) {
return arr[0];
} else {
var result = [];
var allCasesOfRest = allPossibleCases(arr.slice(1)); // recur with the rest of array
for (var i = 0; i < allCasesOfRest.length; i++) {
for (var j = 0; j < arr[0].length; j++) {
result.push(arr[0][j] + allCasesOfRest[i]);
}
}
return result;
}
}
console.log(allPossibleCases(allArrays))
You can also make it with loops, but it will be a bit tricky and will require implementing your own analogue of stack.
I suggest a simple recursive generator function as follows:
// Generate cartesian product of given iterables:
function* cartesian(head, ...tail) {
let remainder = tail.length ? cartesian(...tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
// Example:
const first = ['a', 'b', 'c', 'd'];
const second = ['e'];
const third = ['f', 'g', 'h', 'i', 'j'];
console.log(...cartesian(first, second, third));
You don't need recursion, or heavily nested loops, or even to generate/store the whole array of permutations in memory.
Since the number of permutations is the product of the lengths of each of the arrays (call this numPerms), you can create a function getPermutation(n) that returns a unique permutation between index 0 and numPerms - 1 by calculating the indices it needs to retrieve its characters from, based on n.
How is this done? If you think of creating permutations on arrays each containing: [0, 1, 2, ... 9] it's very simple... the 245th permutation (n=245) is "245", rather intuitively, or:
arrayHundreds[Math.floor(n / 100) % 10]
+ arrayTens[Math.floor(n / 10) % 10]
+ arrayOnes[Math.floor(n / 1) % 10]
The complication in your problem is that array sizes differ. We can work around this by replacing the n/100, n/10, etc... with other divisors. We can easily pre-calculate an array of divisors for this purpose. In the above example, the divisor of 100 was equal to arrayTens.length * arrayOnes.length. Therefore we can calculate the divisor for a given array to be the product of the lengths of the remaining arrays. The very last array always has a divisor of 1. Also, instead of modding by 10, we mod by the length of the current array.
Example code is below:
var allArrays = [first, second, third, ...];
// Pre-calculate divisors
var divisors = [];
for (var i = allArrays.length - 1; i >= 0; i--) {
divisors[i] = divisors[i + 1] ? divisors[i + 1] * allArrays[i + 1].length : 1;
}
function getPermutation(n) {
var result = "", curArray;
for (var i = 0; i < allArrays.length; i++) {
curArray = allArrays[i];
result += curArray[Math.floor(n / divisors[i]) % curArray.length];
}
return result;
}
Provided answers looks too difficult for me. So my solution is:
var allArrays = new Array(['a', 'b'], ['c', 'z'], ['d', 'e', 'f']);
function getPermutation(array, prefix) {
prefix = prefix || '';
if (!array.length) {
return prefix;
}
var result = array[0].reduce(function(result, value) {
return result.concat(getPermutation(array.slice(1), prefix + value));
}, []);
return result;
}
console.log(getPermutation(allArrays));
You could take a single line approach by generating a cartesian product.
result = items.reduce(
(a, b) => a.reduce(
(r, v) => r.concat(b.map(w => [].concat(v, w))),
[]
)
);
var items = [['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j']],
result = items.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));
console.log(result.map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Copy of le_m's Answer to take Array of Arrays directly:
function *combinations(arrOfArr) {
let [head, ...tail] = arrOfArr
let remainder = tail.length ? combinations(tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
Hope it saves someone's time.
You can use a typical backtracking:
function cartesianProductConcatenate(arr) {
var data = new Array(arr.length);
return (function* recursive(pos) {
if(pos === arr.length) yield data.join('');
else for(var i=0; i<arr[pos].length; ++i) {
data[pos] = arr[pos][i];
yield* recursive(pos+1);
}
})(0);
}
I used generator functions to avoid allocating all the results simultaneously, but if you want you can
[...cartesianProductConcatenate([['a', 'b'], ['c', 'z'], ['d', 'e', 'f']])];
// ["acd","ace","acf","azd","aze","azf","bcd","bce","bcf","bzd","bze","bzf"]
Easiest way to find the Combinations
const arr1= [ 'a', 'b', 'c', 'd' ];
const arr2= [ '1', '2', '3' ];
const arr3= [ 'x', 'y', ];
const all = [arr1, arr2, arr3];
const output = all.reduce((acc, cu) => {
let ret = [];
acc.map(obj => {
cu.map(obj_1 => {
ret.push(obj + '-' + obj_1)
});
});
return ret;
})
console.log(output);
If you're looking for a flow-compatible function that can handle two dimensional arrays with any item type, you can use the function below.
const getUniqueCombinations = <T>(items : Array<Array<T>>, prepend : Array<T> = []) : Array<Array<T>> => {
if(!items || items.length === 0) return [prepend];
let out = [];
for(let i = 0; i < items[0].length; i++){
out = [...out, ...getUniqueCombinations(items.slice(1), [...prepend, items[0][i]])];
}
return out;
}
A visualisation of the operation:
in:
[
[Obj1, Obj2, Obj3],
[Obj4, Obj5],
[Obj6, Obj7]
]
out:
[
[Obj1, Obj4, Obj6 ],
[Obj1, Obj4, Obj7 ],
[Obj1, Obj5, Obj6 ],
[Obj1, Obj5, Obj7 ],
[Obj2, Obj4, Obj6 ],
[Obj2, Obj4, Obj7 ],
[Obj2, Obj5, Obj6 ],
[Obj2, Obj5, Obj7 ],
[Obj3, Obj4, Obj6 ],
[Obj3, Obj4, Obj7 ],
[Obj3, Obj5, Obj6 ],
[Obj3, Obj5, Obj7 ]
]
You could create a 2D array and reduce it. Then use flatMap to create combinations of strings in the accumulator array and the current array being iterated and concatenate them.
const data = [ ['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j'] ]
const output = data.reduce((acc, cur) => acc.flatMap(c => cur.map(n => c + n)) )
console.log(output)
2021 version of David Tang's great answer
Also inspired with Neil Mountford's answer
const getAllCombinations = (arraysToCombine) => {
const divisors = [];
let permsCount = 1;
for (let i = arraysToCombine.length - 1; i >= 0; i--) {
divisors[i] = divisors[i + 1] ? divisors[i + 1] * arraysToCombine[i + 1].length : 1;
permsCount *= (arraysToCombine[i].length || 1);
}
const getCombination = (n, arrays, divisors) => arrays.reduce((acc, arr, i) => {
acc.push(arr[Math.floor(n / divisors[i]) % arr.length]);
return acc;
}, []);
const combinations = [];
for (let i = 0; i < permsCount; i++) {
combinations.push(getCombination(i, arraysToCombine, divisors));
}
return combinations;
};
console.log(getAllCombinations([['a', 'b'], ['c', 'z'], ['d', 'e', 'f']]));
Benchmarks: https://jsbench.me/gdkmxhm36d/1
Here's a version adapted from the above couple of answers, that produces the results in the order specified in the OP, and returns strings instead of arrays:
function *cartesianProduct(...arrays) {
if (!arrays.length) yield [];
else {
const [tail, ...head] = arrays.reverse();
const beginning = cartesianProduct(...head.reverse());
for (let b of beginning) for (let t of tail) yield b + t;
}
}
const first = ['a', 'b', 'c', 'd'];
const second = ['e'];
const third = ['f', 'g', 'h', 'i', 'j'];
console.log([...cartesianProduct(first, second, third)])
You could use this function too:
const result = (arrayOfArrays) => arrayOfArrays.reduce((t, i) => { let ac = []; for (const ti of t) { for (const ii of i) { ac.push(ti + '/' + ii) } } return ac })
result([['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j']])
// which will output [ 'a/e/f', 'a/e/g', 'a/e/h','a/e/i','a/e/j','b/e/f','b/e/g','b/e/h','b/e/i','b/e/j','c/e/f','c/e/g','c/e/h','c/e/i','c/e/j','d/e/f','d/e/g','d/e/h','d/e/i','d/e/j']
Of course you can remove the + '/' in ac.push(ti + '/' + ii) to eliminate the slash from the final result. And you can replace those for (... of ...) with forEach functions (plus respective semicolon before return ac), whatever of those you are more comfortable with.
An array approach without recursion:
const combinations = [['1', '2', '3'], ['4', '5', '6'], ['7', '8']];
let outputCombinations = combinations[0]
combinations.slice(1).forEach(row => {
outputCombinations = outputCombinations.reduce((acc, existing) =>
acc.concat(row.map(item => existing + item))
, []);
});
console.log(outputCombinations);
let arr1 = [`a`, `b`, `c`];
let arr2 = [`p`, `q`, `r`];
let arr3 = [`x`, `y`, `z`];
let result = [];
arr1.forEach(e1 => {
arr2.forEach(e2 => {
arr3.forEach(e3 => {
result[result.length] = e1 + e2 + e3;
});
});
});
console.log(result);
/*
output:
[
'apx', 'apy', 'apz', 'aqx',
'aqy', 'aqz', 'arx', 'ary',
'arz', 'bpx', 'bpy', 'bpz',
'bqx', 'bqy', 'bqz', 'brx',
'bry', 'brz', 'cpx', 'cpy',
'cpz', 'cqx', 'cqy', 'cqz',
'crx', 'cry', 'crz'
]
*/
A solution without recursion, which also includes a function to retrieve a single combination by its id:
function getCombination(data, i) {
return data.map(group => {
let choice = group[i % group.length]
i = (i / group.length) | 0;
return choice;
});
}
function* combinations(data) {
let count = data.reduce((sum, {length}) => sum * length, 1);
for (let i = 0; i < count; i++) {
yield getCombination(data, i);
}
}
let data = [['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j']];
for (let combination of combinations(data)) {
console.log(...combination);
}

Categories