How to join an n-dimension array with different separators for each level? - javascript

Sample input
Array
Here I am showing a 3-dimension array but the actual number of dimensions vary and is known as n.
[
[
[1,2],
[3,4]
],
[
[5,6],
[7,8]
]
]
Separators
It has the same length (n) as the number of dimensions of the array where the i-th element represent the separator of the i-th level of the array.
[',', '_', '-']
Desired output
1-2_3-4,5-6_7-8
What I've tried
It works for a 3-dimension array but not for a 4-dimension one.
I know what is going wrong with my code but I have no idea how to fix it.
Besides, I think there are simpler and/or more efficient methods.
3-dimension (working)
const array = [[[1,2],[3,4]],[[5,6],[7,8]]];
const separators = [',', '_', '-'];
const _separators = separators.reverse();
let i;
function join(array, first = false) {
const next = Array.isArray(array[0]);
let result;
if (next) {
result = array.map(e => {
if (first) { i = 0; }
return join(e);
});
i++;
result = result.join(_separators[i]);
}
else {
result = array.join(_separators[i]);
}
return result;
}
const result = join(array, true);
console.log(result);
4-dimension (not working properly)
const array = [[[[1,2],[3,4]],[[5,6],[7,8]]],[[['A','B'],['C','D']],[['E','F'],['G','H']]]];
const separators = ['|', ',', '_', '-'];
const _separators = separators.reverse();
let i;
function join(array, first = false) {
const next = Array.isArray(array[0]);
let result;
if (next) {
result = array.map(e => {
if (first) { i = 0; }
return join(e);
});
i++;
result = result.join(_separators[i]);
}
else {
result = array.join(_separators[i]);
}
return result;
}
const result = join(array, true);
console.log(result);
// desired output: 1-2_3-4,5-6_7-8|A-B_C-D,E-F_G-H

Something like this with recursion
const join = (array, separators, depth) => {
if (depth < separators.length -1) {
return array.map(el => join(el, separators, depth + 1)).join(separators[depth]);
} else {
return array.join(separators[depth]);
}
};
{
const array = [[[1,2],[3,4]],[[5,6],[7,8]]];
const separators = [',', '_', '-'];
console.log(join(array, separators, 0));
}
{
const array = [[[[1,2],[3,4]],[[5,6],[7,8]]],[[['A','B'],['C','D']],[['E','F'],['G','H']]]];
const separators = ['|', ',', '_', '-'];
console.log(join(array, separators, 0));
}

Related

Return all possible combinations of array with optional strings

Lets say I have an array keys = ["the?", "orange", "van", "s?"], with '?' at the end of strings to represent that it is optional.
I want a function in javascript generateCombinations(keys) that returns the possible combinations such as :
[["orange","van"],["the","orange","van"],["orange","van","s"],["the","orange","van","s"]]
One possible way of removing '?' is to simply do a replace("?',"").
I have a feeling it might require a recursive function, which I am not yet quite strong in. Help is appreciated!
So far I've tried this:
function isOptionalKey(key) {
return key.endsWith('?');
}
function hasOptionalKey(keys) {
return keys.some(isOptionalKey);
}
function stripOptionalSyntax(key) {
return key.endsWith('?') ? key.slice(0, -1) : key;
}
function generateCombinations(keys) {
if (keys.length === 1) {
return keys;
}
const combinations = [];
const startKey = keys[0];
const restKeys = keys.slice(1);
if (hasOptionalKey(restKeys)) {
const restCombinations = isOptionalKey(startKey)
? generateCombinations(restKeys)
: restKeys;
if (isOptionalKey(startKey)) {
combinations.push(restCombinations);
}
combinations.push(
restCombinations.map((c) => [stripOptionalSyntax(startKey), ...c])
);
} else {
if (isOptionalKey(startKey)) {
combinations.push(restKeys);
}
combinations.push([stripOptionalSyntax(startKey), ...restKeys]);
}
return combinations;
}
You could take a recursive approach by using only the first item of the array and stop if the array is empty.
const
getCombinations = array => {
if (!array.length) return [[]];
const
sub = getCombinations(array.slice(1)),
optional = array[0].endsWith('?'),
raw = optional ? array[0].slice(0, -1) : array[0],
temp = sub.map(a => [raw, ...a]);
return optional
? [...temp, ...sub]
: temp;
};
keys = ["the?", "orange", "van", "s?"],
result = getCombinations(keys);
console.log(result.map(a => a.join(' ')));

Javascript - Count number of consecutive character occurrences

I have been trying to solve this problem for some time now and I have partially managed to achieve it.
I am trying to write a function that will return a nested array, each array element will contain the number of consecutive chars found in a given string.
For example: for the given string "aaaabccaadeeee", the function should return nested array
[[4, a] [1, b] [2, c] [2, a] [1, d][4, e]]
I have managed to write the following function but it returns
[ [ 4, 'b' ], [ 0, 'c' ], [ 1, 'a' ], [ 1, 'd' ], [ 0, 'e' ] ]
What am I doing wrong?
function consecutiveArray(str) {
const chunks = str.split("");
let counter = 0;
const finalArray = [];
let prevItem;
for(chunk of chunks){
if(!prevItem || prevItem === chunk){
counter++
} else {
finalArray.push([counter, chunk])
counter=0;
}
prevItem = chunk;
}
return finalArray;
}
console.log(consecutiveArray('aaaabccaadeeee'))
You can use String.matchAll to match all the groups of characters in your string; then just iterate the matches to produce your desired result:
const str = 'aaaabccaadeeee'
const res = Array.from(str.matchAll(/(.)\1*/g)).map(([m, g]) => [m.length, g])
console.log(res)
Your else clause is wrong, you should push the counter for prevItem and initialize the count to 1. Also, push the final counter the after the loop.
function consecutiveArray(str) {
const chunks = str.split("");
let counter = 0;
const finalArray = [];
let prevItem;
for(chunk of chunks){
if(!prevItem || prevItem === chunk){
counter++
} else {
finalArray.push([counter, prevItem])
counter=1;
}
prevItem = chunk;
}
finalArray.push([counter, prevItem])
return finalArray;
}
console.log(consecutiveArray('aaaabccaadeeee'))
Here is another way of doing it without RegExp:
const str="aaaabccaadeeee";
const res=[];
str.split("").reduce((a,c,i)=>{
if(a!==c) res.push([0,c]);
++res.at(-1)[0];
return c;
},null);
console.log(res);
// for comparison: Nick's solution:
console.log([...str.matchAll(/(.)\1*/g)].map(([m,g])=>[m.length,g]))

What is a Big O of this algorithm?

I wrote the following algorithm and it's pretty horrendous but at the moment it was the only way I was able to solve the problem. I was just wondering what the Big O of this equation is. My best guess is that it is O(n!) since I have the loops inside the filter function. Is this correct?
/*
Scrabble Validator, essentially.
const dictionary = ['apple', 'avocado', 'anteater', 'april', 'basket', 'ball', 'cat', 'cradle'] etc for about 100 or so words
const points = [{a:1}, {b:2}, {c:3}, {d:4}]; etc for all the letters in the alphabet. there are blanks - a blank is worth 0 but can be used in place of any letter
given a string of letters and a dictionary,
1. find all valid anagrams
2. find their point value using a Points object
3. Sort those valid options by highest scoring point
*/
const chars = 'aplpe';
const dictionary = ['apple', 'avocado', 'lap', 'app', 'anteater', 'april', 'basket', 'ball', 'cat', 'cradle'];
const points = [{p:1}, {a:2}, {e:3}, {l:4}];
let pointsMap = {};
points.forEach((point) => pointsMap = { ...pointsMap, ...point });
const charMap = {};
for (let char of chars) {
charMap[char] = charMap[char] + 1 || 1;
}
const matches = dictionary
.filter(word => {
if (word.length > chars.length) return false;
const wordMap = {};
for (let char of word) {
wordMap[char] = wordMap[char] + 1 || 1;
}
for (let char in wordMap) {
if (!charMap[char] || charMap[char] < wordMap[char]) {
return false;
}
}
return true;
})
.map((word) => {
let total = 0;
for (let char of word) {
total += pointsMap[char];
}
return { word, total }
})
.sort((a, b) => a.total > b.total ? -1 : 1)
.map(({ word }) => word);
return matches;

Find the first none-repeated character in a string, what are the mistakes here?

I'm doing some exercises. the question is to find the first none-repeated character in a string.
My idea is: turn string to array. assign array[0] to a new variable, and remove this array[0] from array. Check if this new array contain this variable, if not, return this variable. Else, use filter to remove same value elements and get a new array. repeat the process. the code as following.
const NoneReChar = (str) => {
let tempArr = str.split('');
let start = tempArr[0];
while (true) {
tempArr.shift();
if (!tempArr.includes(start)) {
return start;
} else {
tempArr.filter(char => char !== start);
start = tempArr[0];
}
}
}
console.log(NoneReChar("aaaabbbeccc"))
I was expect output 'e', but I keep getting 'a'...where are the mistakes I made here?
The Array.filter() method doesn't mutate the original array. You need to assign the result of filter to tempArr:
tempArr = tempArr.filter(char => char !== start);
Example:
const NoneReChar = (str) => {
let tempArr = str.split('');
let start = tempArr[0];
while (true) {
tempArr.shift();
if (!tempArr.includes(start)) {
return start;
} else {
tempArr = tempArr.filter(char => char !== start);
start = tempArr[0];
}
}
}
console.log(NoneReChar("aaaabbbeccc"))
However, you don't handle the not found case. To handle it instead of true, the while clause should stop when the array is empty:
const NoneReChar = (str) => {
let tempArr = str.split('');
let start = tempArr[0];
while (tempArr.length) {
tempArr.shift();
if (!tempArr.includes(start)) {
return start;
} else {
tempArr = tempArr.filter(char => char !== start);
start = tempArr[0];
}
}
return null;
}
console.log(NoneReChar("aabbcc"))
Another option is comparing the length of the array before and after filtering. If the length is the same, the item was not repeated:
const NoneReChar = (str) => {
let tempArr = str.split('');
while (tempArr.length) {
const [start, ...rest] = tempArr; // take the 1st item and the rest
tempArr = rest.filter(char => char !== start); // filter out start
if(tempArr.length === rest.length) { // check current and previous arrays, and if the length still matches, start didn't appear again
return start;
}
}
return null;
}
console.log(NoneReChar("aabzbcc"))
const NoneReChar = (str) => {
const tempArr = str.split('');
let result = 'Not Found';
for (let index = 0; index < tempArr.length; index++) {
const firstIndex = tempArr.indexOf(tempArr[index]);
const lastIndex = tempArr.lastIndexOf(tempArr[index]);
if (firstIndex === lastIndex) {
result = tempArr[index];
break;
}
}
return result;
}
console.log(NoneReChar("aaaabbbeccc"));

How could I find intersections in a string array with a variable number of elements?

I have a function that receives an array composed of numerical, comma separated strings as input, then finds the intersectional numbers in those strings and returns a string of similarly comma separated numbers, with no spaces, containing those intersections. If there are no intersections between the two elements, the function will return false.
What I want is to optimize the function so that it can work with a string array that may have more than just two elements. Is that possible? If so, could I have some sort of guideline of where to start looking for answers?
Currently, this is what I have.
function LocateIntersection(strArr) {
let arrHalf1 = strArr[0].split(", ");
let arrHalf2 = strArr[1].split(", ");
let interArr = arrHalf1.filter(value => arrHalf2.includes(value));
let result = interArr.join();
if (result) {
return result;
} else {
return false;
}
}
My answer is a little flawed, but it should meet your requirements
function LocateIntersection(strArr) {
const AllArrHalf = strArr.map((value) => value.split(', ')).sort((a, b) => b.length - a.length);
const lastArrHalf = AllArrHalf[AllArrHalf.length - 1];
let interArr = [];
AllArrHalf.forEach((value, index) => {
if (index !== AllArrHalf.length - 1) {
interArr.push(lastArrHalf.filter(value1 => value.includes(value1)))
}
})
if (interArr.length > 1) {
let result = interArr.map(value => value.join(', '));
LocateIntersection(result);
} else if (interArr.length === 1) {
result = interArr.join();
console.log(result);
}
}
LocateIntersection(['a, b, c', 'a, b', 'a, b'])
You can try this.
const intersection = (arr1, arr2) => {
return arr2.filter(element => arr1.includes(element));
}
const getIntersection = (stringArray, prevResult) => {
const array1 = prevResult || stringArray[0].split(', ');
const array2 = stringArray.shift().split(', ');
const result = intersection(array1, array2);
console.log(`result : `, result)
if(result.length > 0 && stringArray.length > 0) {
return getIntersection(stringArray, result);
}
return result;
}
const input = ['1, 2','1, 3, 3, 3','123, 222','1, 1, 1','1','3, 2, 3, 1'];
const result = getIntersection(input);
console.log('final Result:',result);

Categories