How to split a string and repeat characters? [duplicate] - javascript

This question already has answers here:
How do I replace a character at a particular index in JavaScript?
(30 answers)
Closed 3 years ago.
I'm trying to convert the array = ["A", "B", "C", "D"] into array = ["A", "BB", "CCC", "DDDD"]. If you compared the arrays, you'll notice that the letters inside the second array have extra same letters in each position. Here is my code below. I used the for loop because I wanted to use the increment inside the for loop. I don't even know if map() method can do the same so I went with for loop. However, when I checked, it didn't show the result that I wanted. What did I miss?
function accum(s) {
// your code
let accArray = s.toUpperCase().split("");
for (let i = 1; i < accArray.length; i++) {
accArray[i].repeat(i + 1);
}
console.log(accArray);
}
accum("abcd")

A more concise way to achieve the desired output is to use Arrow Functions:
const accum = (s) => s.toUpperCase()
.split("")
.map((s, i) => s.repeat(i + 1));
console.log(accum("abcd"));

String.prototype.repeat()
The repeat() method constructs and returns a new string which contains the specified number of copies of the string on which it was called, concatenated together.
You have to assign the new returned value at the specific index:
accArray[i] = accArray[i].repeat(i + 1);
function accum(s) {
// your code
let accArray = s.toUpperCase().split("");
for (let i = 1; i < accArray.length; i++) {
accArray[i] = accArray[i].repeat(i + 1);
}
console.log(accArray);
}
accum("abcd")

You need to push values to array, or assign back to that index, repeat does not mutate the original value it returns a new string Repeat so you need to assign it back
function accum(s) {
// your code
let accArray = s.toUpperCase().split("");
let op = []
for (let i = 0; i < accArray.length; i++) {
op.push(accArray[i].repeat(i + 1));
}
console.log(op);
}
accum("abcd")

if your input is already an array like
arr = ['a', 'b', 'c', 'd']
then you can use
arr.map((item, idx) => item.repeat(idx+1))
if you are having string like
str = 'abcd'
then you can use:
str.split('').map((item, idx) => item.repeat(idx+1);

You can use forEach or map both work perfectly as iterator
function accum(s) {
// your code
let accArray = s.toUpperCase().split("");
const arr = [];
accArray.forEach((e, index) => {
arr.push(e.repeat(index + 1))
})
console.log(arr);
}
accum("abcd")

Related

Find unique characters in a string and the count of their occurrences

I need to count the occurrence of characters in a given string and print out the unique characters and the number of how many times they appeared. So, for example, if I receive a string of 'HELLO' it should print out:
H: 1,
E: 1,
L: 2,
O: 1
This is a much-simplified version of a problem, but the answer should put me in the right direction. How can I approach this problem?
Thank you in advance.
This is more or less what it should look like in order to make it easier it prints it in JSON you can already convert it to String yourself if you want.
function count_occurrence(text = "") {
const array_from_text = text.split("");
const result = {};
Array.from(new Set(array_from_text)).forEach(word => {
const { length } = array_from_text.filter(w => w === word);
result[word] = length;
});
return result;
};
const occurences = count_occurence("HELLO");
console.log(occurences); // {H: 1, E: 1, L: 2, O: 1}
You could use Array.reduce() to get a count of each occurrence in the input word.
We convert the word to an array using the ... operator, then .reduce() to create an object with a property for each unique letter in the word.
const input = 'HELLO';
const result = [...input].reduce((acc, chr) => {
acc[chr] = (acc[chr] || 0) + 1;
return acc;
}, {});
console.log('Result:', result)
.as-console-wrapper { max-height: 100% !important; }
const countChars = (str) => {
const charCount = {} ;
for (const c of [...str]) {
charCount[c] = (charCount[c] || 0) + 1 ;
}
return charCount ;
}
console.log(countChars('HELLO')) ; // {H: 1, E: 1, L: 2, O: 1}
My approach to this problem is:
let str = "HELLO";
// An object for the final result {character:count}
let counts = {};
// Loop through the str...
for (let index = 0; index < str.length; ++index) {
// Get each char
let ch = str.charAt(index);
// Get the count for that char
let count = counts[ch];
// If we have one, store that count plus one;
if (count) {
counts[ch] += 1;
} else {
// if not, store one
counts[ch] = 1;
}
// or more simply with ternary operator
// counts[ch] = count ? count + 1 : 1;.
}
console.log(counts);
Maybe the easiest answer is just split to char and put it into the map.
const count={}
"HELLO".split("").forEach(e=>{
count[e]??=0;
count[e]++;
})
count is what you want.
Use a dictionary like datastructure that gives you O(1) access and update times. In JS you can use an Object literat (not recommended) or a Map.
Iterate over the characters of your string and update the dictionary by incrementing the character count of the current character. If it isn't in your dictionary add it and set the count to one.
When done with the iteration, iterate over the keys of your dictionary, where the values are the the number of occurence of that specific character, and output them in any format of your liking.
const myStr = "Hello"
const myMap = new Map()
for (let c of myStr) {
if (myMap.has(c)) {
myMap.set(c, myMap.get(c)+1)
} else {
myMap.set(c, 1)
}
}
for (let k of myMap.keys()) {
console.log(`${k} occures ${myMap.get(k)} times`)
}

Splitting string into array based on first and last

I have this array :-
var a = [' DL1,C1,C5,C6','M4,DL3-7,B1-5']
And I want to split them like
[DL1,C1,C5,C6,M4,DL3,DL4,DL5,DL6,DL7,B1,B2,B3,B4,B5]
So that DL3-7 or DL3-DL7 this Split like this DL3,DL4,DL5,DL6,DL7
Reason why I am doing this, is because I want to block duplicate entry like DL3 should not come anywhere else, I am trying for loops to do this, just want to know if there is any simpler way to do it, and check for duplicacy afterwards.
Thanks
You have to break down your problems into three parts:
getting comma delimited values into different array items
resolving "DL3-7" to "DL3", "DL4"...
removing duplicates
Once you break down the problem, it is much easier to handle them one by one. The code is pretty readable, let me know if there is anything difficult to understand what's going on.
const a = ['DL1,C1,C5,C6', 'M4,DL3-7,B1-5']
//this will split all comma delimited values
const commaDelimit = a.map(item => item.split(',')).flat();
console.log("Separate values by comma: ")
console.log(commaDelimit);
//this will turn the ranges into individual items
//this does not account for if the number is bigger than 9.
//you can try doing this part yourself if you need to, should be a good learning exercise.
const resolveRange = commaDelimit.map(item => {
if (item.includes('-')) {
const pos = item.indexOf('-');
const beginning = Number(item.charAt(pos - 1));
const end = Number(item.charAt(pos + 1)) + 1;
const toReturn = [];
const prependString = item.substring(0, pos - 1);
for (let i = beginning; i < end; i++) {
toReturn.push(`${prependString}${i}`)
}
return toReturn;
}
return item;
}).flat();
console.log("Change 'DL3-7' to DL3, DL4 and so on: ")
console.log(resolveRange);
//this will get rid of duplicates
const uniques = [...new Set(resolveRange)];
console.log("Remove duplicates: ")
console.log(uniques);
Create an Array with that length, iterate and transform,
I've just wrote the most challenged part:
function splitRange(range) {
let a = range.split('-');
if (a.length < 2) return [range];
const baseString = (a[0].match(/[a-z A-Z]/g))?.join('');
const baseNumber = +((a[0].match(/\d+/))?.shift());
return Array.from({length: +a.pop().match(/\d+/) - baseNumber + 1}).map((_,i)=>`${baseString}${i+baseNumber}`);
}
const s='DL1,C1,C5,C6,M4,DL3-7,B1-5';
console.log(
s.split(',').map(item=>splitRange(item)).flat()
);
Basically, #cSharp has explained the concept of data transformation to the desired output.
Split by comma.
Work with regex to transform the range value and append it to the array. Regex pattern & test data
Distinct the array value.
var a = [' DL1,C1,C5,C6','M4,DL3-7,B1-5'];
var formatteds = a.reduce((previous, current) => {
var splits = current.trim().split(',');
var rangedSplits = splits.reduce((prev, cur) => {
var pattern = new RegExp(/([A-Z]*)(\d)-[A-Z]*(\d)/);
var match = pattern.exec(cur);
if (match) {
// Pattern 1: ['DL3-7', 'DL', '3', '7']
// Pattern 2: ['DL3-DL7', 'DL', '3', '7']
var startIndex = parseInt(match[2].toString());
var endIndex = parseInt(match[3].toString());
var arr = [];
for (let i = startIndex; i <= endIndex; i++) {
arr.push(match[1].toString() + i);
}
prev = prev.concat(arr);
} else {
prev = prev.concat([cur]);
}
return prev;
}, []);
previous = previous.concat(rangedSplits);
return previous;
}, []);
var result = formatteds.filter((x, i, array) => array.indexOf(x) === i);
console.log(result);

Can I have an object key as part of an array?

I am trying to sort some numbers, and I want to count the number of times that an array has a certain number.
My question is more about the structure of an the array than the counting the number part. I would like to build an array that looks like this below.
let numbers = [1,2,3,4,4,4,5,5,6];
How could I make the data structure below?
numbers[3].count // this will be equal to 3 after I loop through;
How do I make each part of the array have an object parameter?
Do I just loop through like so?
for (let i = 0; i < numbers.length; i++){
numbers[i] = {
count: 0
}
}
I understand this wont give me the right count, but I don't care about that part of the problem. I would like to solve that on my own. I just need to be sure that this is the correct way to add the object parameters.
I would build these functions on my own. Something like this
You can copy and paste this in the console of your browser.
// my numbers list
const numbers = [1, 2, 3, 4, 4, 4, 5, 5, 6];
// reduced to unique entries
const uniques = [...new Set(numbers)];
// function to count occurrences in my list of number
const count = (n) => numbers.filter((num) => num === n).length;
// you can test here
console.log(`counting ${uniques[4]}s`, count(uniques[4]));
// get these as object
console.log(uniques.map((unique) => ({[unique]: count(unique)})))
Simplest way to achieve this by using Array.forEach().
let numbers = [1,2,3,4,4,4,5,5,6];
const obj = {};
numbers.forEach((item) => {
obj[item] = (obj[item] || 0) + 1;
});
console.log(obj);
const numbers = [1,2,3,4,4,4,5,5,6];
const counts = {};
numbers.forEach((x) => counts[x] = (counts[x] || 0) + 1);
console.log(counts)
You can use the Array#reduce() method to group elements together into sub arrays. Since arrays have a length property that gives the the number of elements this can be applied to each group of like elements. We do not need to create a new count property.
let numbers = [1,2,3,4,4,4,5,5,6];
const freq = numbers.reduce(
(acc,cur) => ({...acc,[cur]:(acc[cur] || []).concat(cur)})
);
console.log( freq[4] );
console.log( freq[4].length );
Alternatively, you can put the numbers in an object and get all the unique elements, then for each unique element define a get property that groups like elements together using the Array#filter() method. Again, the length array property can be used to return the number of elements for each unique element.
const o = {numbers: [1,2,3,4,4,4,5,5,6]};
o.uniq = [...new Set(o.numbers)];
o.uniq.forEach(n => Object.defineProperty(
o,n,{get:() => o.numbers.filter(num => num === n)}
));
console.log( o[5] );
console.log( o[5].length );
Reduce is perfect for these kinds of problems.
const numbers = [1,2,3,4,4,4,5,5,6];
const countedObject = numbers.reduce((tmpObj, number) => {
if (!tmpObj[number]) {
tmpObj[number] = 1;
} else {
tmpObj[number] += 1;
}
return tmpObj
}, {});
console.log(countedObject);
if you feel the need to nest it further you can of course do this.
But if count is the only property you need, I'd suggest sticking to the first version.
const numbers = [1,2,3,4,4,4,5,5,6];
const countedObject = numbers.reduce((tmpObj, number) => {
if (!tmpObj[number]) {
tmpObj[number] = {count: 1};
} else {
tmpObj[number].count += 1;
}
return tmpObj
}, {});
console.log(countedObject);

Concise way to return a new array of N elements filled with iterated values from another array? Vanilla JavaScript

I have a given array with an undetermined quantity of elements, the array can be numbers or strings, then I need to generate a new array of N elements made from the iterated elements of the first array
I already have a function to do it, but it only works if the original array are consecutive numbers, it doesn't work with strings. I have a gazillion of ideas on how to achieve it. I could just concatenate the array to a new one until its equal or greater than the required quantity of elements, and then set the new array length to the required quantity, but is there a more concise and elegant way to do it?
IDEA 01 codepen
function populateArray(qty) {
// Array to populate from
let array = [1,2,3];
//Determine the Range Length of the array and assign it to a variable
let min = array[0];
let max = array[array.length - 1];
const rangeLength = (max - min + 1);
//Initialize uniqueArray which will contain the concatenated array
let uniqueArray = [];
//Test if quantity is greater than the range length and if it is,
//concatenate the array to itself until the new array has equal number of elements or greater
if (qty > rangeLength) {
//Create an array from the expansion of the range
let rangeExpanded = Array.from(new Array(rangeLength), (x,i) => i + min);
while (uniqueArray.length < qty) {
uniqueArray = uniqueArray.concat(rangeExpanded);
}
}
// Remove additional elements
uniqueArray.length = qty
return uniqueArray;
}
console.log(populateArray(13))
IDEA 02 codepen, but it fills the new array 13 times with the whole original array, not iterated items
// FILL A NEW ARRAY WITH N ELEMENTS FROM ANOTHER ARRAY
let array = [1,2,3];
let length = 13;
let result = Array.from( { length }, () => array );
console.log(result);
the expected result is [1,2,3,1,2,3,1,2,3,1,2,3,1] if the original array were made of strings the expected result would be [dog,cat,sheep,dog,cat,sheep,dog,cat,sheep,dog,cat,sheep,dog]
You can tweak your second idea a bit - calculate the number of times you need to repeat the initial array to come up with the required number of total items, then flatten it and .slice:
let array = [1,2,3];
let length = 13;
const fromLength = Math.ceil(length / array.length);
let result = Array.from( { length: fromLength }, () => array )
.flat()
.slice(0, length);
console.log(result);
I'll go with #CertainPerformance's answer. But here's a different approach, just for thinking-out-of-the-box purposes
// A function for getting an index up to length's size
function getIDX(idx, length){
return idx <= length ? idx : getIDX(idx-length, length);
}
const newArrayLength = 13;
const sourceArray = [1,2,3];
const resultArray = [];
for(let i=0; i< newArrayLength; i++){
resultArray[i]=sourceArray[getIDX(i+1, sourceArray.length)-1];
}
EDIT 1:
I was comparing the performance of this approach versus the others here described and it seems that if you wanted to create a very large new array (ex: newArrayLength= 10000) the getIDX() function takes a lot to finish because of the size of the call stack. So I've improved the getIDX() function by removing the recursion and now the complexity is O(1) check it out:
function getIDX(idx, length){
if (length === 1) {return idx};
const magicNumber = length * (Math.ceil(idx/length)-1);
return idx - magicNumber;
}
With the new getIDX() function this approach seems to be the most performant.
You can take a look to the tests here:
https://jsbench.me/v7k4sjrsuw/1
You can use a generator function that will create a repeating sequence from an input. You can add a limit to the generated sequence and simply turn it into an array:
function* repeatingSequence(arr, limit) {
for(let i = 0; i < limit; i++) {
const index = i % arr.length;
yield arr[index];
}
}
const generator = repeatingSequence(["dog", "cat", "sheep"], 10);
const result = Array.from(generator);
console.log(result);
Alternatively, you can make an infinite repeating sequence with no limit and then generate as many elements as you want for an array:
function* repeatingSequence(arr) {
let i = 0
while(true) {
const index = i % arr.length;
yield arr[index];
i++;
}
}
const generator = repeatingSequence(["dog", "cat", "sheep"]);
const result = Array.from({length: 10}, () => generator.next().value);
console.log(result);
You can use modulo operator. Special thanks to #Vlaz for shorten version:
Array.from({ length:length }, (e, i) => array[ i % array.length ])
An example:
let array = [1,2,3];
let length = 13;
const result = Array.from({ length:length },
(e, i) => array[ i % array.length ]);
console.log(result);

How to remove certain elements from an array into a new array and leave the others only the original array?

How to write a function to remove certain elements into a new array and leave the original array with only the remaining elements?
the first part is easy using a for loop pushing the even numbers into a new array but mutating the original array to leave only the odd numbers is hard
function remove(arr, cb){
var removed = [];
var newArr = [];
for(var i = 0; i < arr.length; i++) {
if(cb(arr[i], i, arr)) {
removed.push(arr[i]);
}
}
return removed;
}
Use an else statement to fill newArr with values that should stay in the original arr, then empty it using splice() before copying the items from newArr back into it.
function remove (arr, cb) {
var removed = [];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (cb(arr[i], i, arr)) {
removed.push(arr[i]);
} else {
newArr.push(arr[i]);
}
}
arr.splice(0);
for (var i = 0; i < newArr.length; i++) {
arr.push(newArr[i]);
}
return removed;
}
Welcome to Stackoverflow!
Personally, I'd avoid anything that mutates an input parameter, as this increases code complexity and makes it hard to reason about what's happening from the calling side.
Instead, I'd write a method that returns an array of two arrays. This can be easily split into two variables at the calling end using by using array destructuring.
See the example below:
const splitArr = (arr, pred) =>
arr.reduce(
(prev, curr, idx) => {
prev[+pred(curr, idx, arr)].push(curr);
return prev;
}, [[], []]
);
// usage //
const myArr = [1, 2, 3, 4];
const [arr1, arr2] = splitArr(myArr, x => x > 2);
console.log(arr1);
console.log(arr2);
Because pred is a function that returns a boolean value, we can co-erce this value to 0 or 1 using +someBoolean. We can then use this value as an index to decide into which of the two output arrays the value should be pushed.
You were definitely on the right track with your solution, a couple tweaks and we can make it very readable and also very easy to work with. I tried to keep the format of what it looked like you were doing.
I do take advantage of destructuring here, this could be returned as just an object, and then reference the properties.
const myArr = [0,1,2,3,4,5,6,7,8,9,10];
const splitItems = (arr, logicFunc) => {
let secondSet = []
const firstSet = arr.filter(v => {
if (logicFunc(v)) return true
else secondSet.push(v)
})
return { firstSet, secondSet }
}
const myLogicFunc = v => (v < 3 || v == 9)
const { firstSet, secondSet } = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${firstSet}`) // My first set: 0,1,2,9
console.log(`My second set: ${secondSet}`) // My second set: 3,4,5,6,7,8,10
/* OR without destructuring:
const myArrays = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${myArrays.firstSet}`)
console.log(`My second set: ${myArrays.secondSet}`)
*/
Please let me know if you have any questions
In modern JavaScript apps we do not mutate arrays we create new array, this avoids side effects, so what we do is create two new arrays
const split = (source, conditionFunc) = [ source.filter(i => conditionFunc(i)), source.filter(i => !conditionFunc(i))];
Then you have an array of two arrays of the values that meed condition and those that don't and you have not caused any side effects.
const odssAndEvens = split(source, i => i % 2 === 1);
Or with reduce so you don't iterate the array twice
const split = (source, conditionFunc) = source.reduce((results, item) => {
if (conditionFunc(item)) {
results[0].push(item);
} else {
results[1].push(item);
}
return results;
}, [[],[]]);

Categories

Resources