Scramble String According to Array Values - Javascript - javascript

Trying to solve this question on Codewars.
I've seen other articles that deal with shuffling / scrambling a string randomly.
But what about scrambling a string according to the values in a given array?
I.e. abcd given the array [0, 3, 2, 1] will become acdb because:
a moves to index 0
b moves to index 3
c moves to index 2
d moves to index 1
My guess is to start out by splitting the string into an array. And then we want to get the index value of the array that's passed into the scramble function, and push the character at the index value from that array into the new array. And finally join the array:
function scramble(str, arr) {
let newArray = str.split("");
let finalArray = [];
for (let i = 0; i < str.length; i++) {
console.log(newArray);
finalArray.push(newArray.splice(arr[i], 1));
}
return finalArray;
}
console.log(scramble("abcd", [0, 3, 1, 2]));
But the problem with this logic is that .splice() removes the character from the newArray every time.
Is there another method that will remove the character at the specified index without modifying the original array?
I don't think slice will work either.

You can make a separate array to store the letters.
var str = "abcd";
var arr = [0, 3, 2, 1];
function scramble(s, a) {
var ar = Array(a.length);
a.forEach((e, i) => {
ar[e] = s[i];
});
return ar.join('');
}
console.log(scramble(str, arr));

Answer
Use reduce on the array and as the array is iterated assign the character of the iterator index(i) in the string(s) to the value index(v) of the new array(ra). After the reduce completes, use join to turn the returned array(ra) back into a string.
let scramble = (s, a) => a.reduce((ra,v,i)=> (ra[v] = s[i], ra), []).join("");
Example:
let scramble = (s, a) => a.reduce((ra,v,i)=> (ra[v] = s[i], ra), []).join("");
console.log( scramble("abcd", [0,3,2,1]) );
Clarification Code:
I realize the above code may be hard to wrap your head around. Let me provide you with the same exact functionality, but in a standard function. Keep in mind this is exactly what is happening in the above code, but it may be simpler to comprehend if you're not used to the concision of ES6:
function scramble(my_string, my_array) {
// create an array to return
let returnable_array = [];
// loop through the provided array.
// string index is the array key: 0,1,2,3
// array_index is the value of the array keys: 0,3,2,1
for(let [string_index, array_index] of my_array.entries()) {
// we assign the character at string index
// to the value index inside the returnable array
returnable_array[array_index] = my_string[string_index];
}
// we turn the array into a string
let returnable_string = returnable_array.join("");
// we return the string
return returnable_string
}
Example:
function scramble(my_string, my_array) {
let returnable_array = [];
for(let [string_index, array_index] of my_array.entries()) {
returnable_array[array_index] = my_string[string_index];
}
returnable_string = returnable_array.join("");
return returnable_string
}
console.log(scramble("abcd", [0,3,1,2]));

You can loop over the input string get the character at the current position using string.charAt(position) and put it into a new array into the position retrieved from the positions array.
function scramble (str, arr) {
let newArray = [];
for (let i = 0; i < str.length; i++) {
newArray[arr[i]]=str.charAt(i);
}
return newArray.join();
}
console.log(scramble("abcd", [0, 3, 1, 2]));

I think the best approach would be to put the string into a new array:
function scramble(str, arr) {
//validate if the array has elements
if (arr && arr.length) {
//create a new array
const strArr = []
arr.forEach(index => {
//push each character by index
//As noted by Barmar you could either use
//str.charAt(index) or str[index]
//as both will return the character at the specified index
strArr.push(str.charAt(index))
})
//return a new string
return strArr.join('');
}
}

Related

Repeat character depending on position in array + 1

I'm trying to get an output like this:
['h', 'ee', 'lll', 'llll', 'ooooo']
currently my out put is:
[ 'h', 'ee', 'lll', 'lll', 'ooooo' ]
The issue is the second occurrence of the "l" isn't being repeated one more time because I'm counting the index of the letter then adding 1 and it is only counting the first occurrence of the "l".
This is the code I have so far, any help would be great.
function mumble(string) {
string.toLowerCase();
let arrayPush = [];
let array = string.split("");
let count = 0;
let char = [];
array.map((letter) => {
count = array.indexOf(letter);
arrayPush.push(letter.repeat(count + 1));
});
return arrayPush;
}
console.log(mumble("hello"));
Don't use indexOf, use the second parameter in the .map callback to determine the index (and from that, the number of times to repeat the string).
function mumble(string) {
string.toLowerCase();
let arrayPush = [];
let array = string.split("");
let count = 0;
let char = [];
array.map((letter, i) => {
arrayPush.push(letter.repeat(i + 1));
});
return arrayPush;
}
console.log(mumble("hello"));
Instead of applying .map to an array split from the string, you can simply loop through the string, accessing each character using the .charAt() string method, and pushing it's .repeat() product to the result array.
Working snippet:
console.log(mumble('hELlo'));
function mumble(string) {
const result = [];
for(let i=0; i<string.length; i++) {
result.push(string.toLowerCase().charAt(i).repeat(i+1));
}
return result;
} // end function mumble;

How to split an array into multple arrays each with a unique name

I have an array = [A,1,0,1,0,1,B,1,0,0,1,A,1]
I need to split this array into multiple arrays. The split will occur at the "A" or "B" position as seen in the new arrays below. The names of the new arrays use the string "group" plus an incremented number starting with 1 or 0.
The end result should look like:
group1 = [A,1,0,1,0,1]
group2 = [B,1,0,0,1]
group3 = [A,1]
I can get the section of the array I need by creating an array (arrTemp), so I can store the positions (indexes) and later use slice() to get the sections I want (A,1,0,1,0,1), (A,1,0,0,1), and (A,1). But I don't know how to store the results of my slice()'s in arrays with unique names incremented by 1.
This is what I have tried so far:
var arr = [A,1,0,1,0,1,B,1,0,0,1,A,1];
arr.forEach(myFunction)
function myFunction(item, index) {
if ((item=="A") || (item=="B")) {
arrTemp.push(index);
arrTemp=arrTemp; //not sure I need this. I did this so it array would be global
}
}
for (var i = 0; i < arr.length; i++){
sectArray = arr.slice(arrTemp[i]+1,arrTemp[i + 1])
'group' + [i] = [arrTemp[i],sectArray]; //here is my problem.
}
It seems like you're trying to dynamically create variables. That seems tricky and probably won't work. What you should probably have is some collection of results. Probably a parent array that holds all of them.
For example:
var containerArray = [];
Then:
for (var i = 0; i < arr.length; i++){
sectArray = arr.slice(arrTemp[i]+1,arrTemp[i + 1])
containerArray[i] = [arrTemp[i],sectArray];
}
Now containerArray will have all of your stuff. You can also do this with an object:
var containerObject = {};
And the same thing after.
you only need one loop here, keep an empty temp array, iterate over arr and keep pushing elements in temp each time you see 'A' or 'B' push temp to final array, and at last push temp once more into final array because last section will be left.
var arr = ['A',1,0,1,0,1,'B',1,0,0,1,'A',1];
var temp = [];
var sectArray = [];
arr.forEach(myFunction)
function myFunction(item, index) {
if (((item=="A") || (item=="B")) && temp.length) {
sectArray.push(temp);
temp = [item];
}else{
temp.push(item);
}
}
sectArray.push(temp);
console.log(sectArray);
Check this solution that use a combination of string and array methods:
var data = ['A',1,0,1,0,1,'B',1,0,0,1,'A',1];
var results = data.toString().split(/(?=[a-zA-Z]+)/)
.map(function(value){
return value.split(',').filter(function (item) {
return item.length ? true: false;
})
})
.map(function(item) {
return item.map(function (value) {
return isNaN(parseInt(value)) ? value : parseInt(value);
})
});
console.log(results);
// results = [["A", 1, 0, 1, 0, 1], ["B", 1, 0, 0, 1], ["A", 1]]
Another solution using Array#reduce function.
var x = ["A", 1, 0, 1, 0, 1, "B", 1, 0, 0, 1, "A", 1];
function reformat(arr) {
var smallArrCounter = 0;
return arr.reduce(function (acc, item) {
if (item === "A" || item === "B") {
acc["group" + (++smallArrCounter)] = [item];
} else {
acc["group" + smallArrCounter].push(item);
}
return acc;
}, {});
}
var result = reformat(x);
console.log(result.group1); // ["A", 1, 0, 1, 0, 1]
console.log(result.group2); // ["B", 1, 0, 0, 1]
console.log(result.group3); // ["A", 1]
There may be a more performant approach that doesn't require two iterations of the array, but my thought is:
Determine the indices of the group delimiters (characters)
Slice the array into groups based on those delimiters, using either the next index as the end, or arr.length if slicing the last group
This has the assumption that the array delimiters may not be known in advance.
const charIndices = [];
const groups = [];
const arr = ['A',1,0,1,0,1,'B',1,0,0,1,'A',1];
// get the indices of the characters
arr.forEach((v, i) => ('' + v).match(/[A-Z]+/) ? charIndices.push(i) : undefined);
// use the found indices to split into groups
charIndices.reduce((a, b, i) => {
a.push(arr.slice(b, charIndices[i+1] ? charIndices[i+1]-1 : arr.length));
return a;
}, groups);
console.log(groups);

sorting a string of numbers that each have a letter inside. making sure to keep the same position of duplicates as in the original array

I have a string of numbers. each number has a letter randomly placed inside of the number. I need to sort those numbers based on the letter inside of each number alphabetically. However if there are duplicates, I have to keep the order from the original string. for example: "c21 32b 43a 2c3" must be sorted to "43a 32b c21 2c3" then i need to strip the letters out.
here is what i have so far:
function cats(s) {
let arr = s.split(' ').sort(function (a,b) {
return a.match(/[a-z]/i)[0].localeCompare(b.match(/[a-z]/i)[0]);
});
console.log(arr)
for (i = 0; i < arr.length; i++) {
arr[i] = arr[i].replace(/\D/g,'');
}
console.log(arr)
}
cats('y381 6a684 9c94 5x346 c9541 31w1 440x16 x620 1b33 y4773 c3019');
I can't seem to get the duplicate letters to sort correctly. this is important because later on i need to perform math on these numbers and the order is important to get the correct solution.
You can keep an array of the split strings as a reference and use the index to sort by when the letters match
let refArr = s.split(' ');
let arr = refArr.slice().sort(function (a,b) {
let aLetter = a.match(/[a-z]/i)[0], bLetter=b.match(/[a-z]/i)[0];
if(aLetter === bLetter){
return refArr.indexOf(a) - refArr.indexOf(b);
}else{
return aLetter.localeCompare(bLetter);
}
});
You just require a small mod, basically do a map before the sort, to store the array index, the sort then can use the index to do a composite sort, and then we use map to put back into the format you had before.
function cats(s) {
let arr = s.split(' ').
map(function (v,i) { return {v:v, i:i }}).
sort(function (a,b) {
return a.v.match(/[a-z]/i)[0].localeCompare(b.v.match(/[a-z]/i)[0]) || a.i - b.i}).
map(function (v) { return v.v; });
console.log(arr)
for (i = 0; i < arr.length; i++) {
arr[i] = arr[i].replace(/\D/g,'');
}
console.log(arr)
}
cats('y381 6a684 9c94 5x346 c9541 31w1 440x16 x620 1b33 y4773 c3019');
You could use sorting with map and get a new array with the sorted items. This maintains the order of the items with same letters.
var array = 'y381 6a684 9c94 5x346 c9541 31w1 440x16 x620 1b33 y4773 c3019'.split(' ');
var mapped = array.map((el, i) => ({ index: i, letter: el.match(/[a-z]/i)[0] }));
// sorting the mapped array containing the reduced values
mapped.sort((a, b) => a.letter.localeCompare(b.letter) || a.index - b.index);
// container for the resulting order
var result = mapped.map(el => array[el.index]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Convert number to a reversed array of digits

I've developed this codepen (http://codepen.io/PiotrBerebecki/pen/qZjojV?editors=0010) trying to solve the following JavaScript problem:
Given a non-negative integer, return an array containing a list of independent digits in reverse order.
Example:
348597 => The correct solution should be [7,9,5,8,4,3]
The function below apparently is incorrect as it returns ["7", "9", "5", "8", "4", "3"] - correct order but with quotes. How could I modify it so that it returns [7,9,5,8,4,3]?
function digitize(n) {
var initialArray = (""+n).split('');
var reversedArray = [];
for (var i = initialArray.length - 1; i >= 0; i--) {
reversedArray[i] = initialArray.shift();
}
return reversedArray;
}
"One-line" solution:
var num = 348597,
arr = String(num).split("").reverse().map(Number);
console.log(arr); // [7, 9, 5, 8, 4, 3]
String(num) : The String global object acts as a constructor for strings and "converts" the given number into string(in this case)
The Array.reverse(): method reverses an array in place
The Array.map(): method creates and returns a new array calling a provided function on every array element
add parseInt to convert from string to number, since when you split you turn every integer into a string
function digitize(n) {
var initialArray = (""+n).split('');
var reversedArray = [];
for (var i = initialArray.length - 1; i >= 0; i--) {
reversedArray[i] = parseInt(initialArray.shift(),10);
}
return reversedArray;
}
console.log(digitize(348597));
Even better, reduce it to two lines:
function digitize(num) {
return num.toString().split('').reverse().map(Number);
}
The final map call applies a function to each element in the array (in this case the function converts a string to an object) - everything else simply converts the number to a string, splits the string to an array and reverses it.
Traditionally, parseInt would be used in the map call, but this gives rise to strange behaviour.
Just split and reverse
var num = 348597,
arr = num.toString().split("").reverse().map(Number);
document.write('<pre>' + JSON.stringify(arr, 0, 4) + '</pre>');
It is a simplified version of many other solutions that you can see, to deep understand how it works.
function digitize(n) {
let correctArr = [];
let arrOfNumbers = n.toString().split('');
let arrOfNumbersLength = arrOfNumbers.length;
for (let i = 0; i < arrOfNumbersLength; i++) {
let x = arrOfNumbers.pop();
correctArr.push(+x);
}
return correctArr;
}
console.log(digitize(348597));
If you care about performance, here is the one.
var num = 348597;
var digits = num + '';
var result = [];
for (var i = 0, length = digits.length; i < length; i++) {
result[length - 1 - i] = +digits[i];
}
console.log(result);
For beginners who may want to see a clear format on how to solve this via plain English, this may help to understand it:
function reverseNumber(num){
num = num + '';
let reversedText = num.split('').reverse().join('');
let reversedNumber = parseInt(reversedText, 10);
console.log("reversed number: ", reversedNumber);
return reversedNumber;
}
console.log(reverseNumber(12345));
A simpler way to solve this is below.
function digitize(n) {
numbers = n.toString()//convert n to a string
arrayNum = numbers.split('') //split the string and make an array
arrayRev =arrayNum.reverse()//reverse the new array made.
newArr = arrayRev.map(Number) // The Number constructor contains constants and methods for working with numbers. Values of other types can be converted to numbers using the Number() function.
return newArr;
}
Refactored
function digitize(n) {
return n.toString().split('').reverse().map(Number)
}

Insert number A at index B

Could you please help me figure out this algorithm without using splice:
Write a program that inserts a new number A at an index B. For example, if array = [1, 3, 5, 7] and A = 10 and B = 2, by the end of your program, array should be [1, 3, 10, 5, 7].
My thinking is that I would loop through the array, and then replace A with B, but it's not quite right:
for(var i = 0; i < arr.length; i++) {
arr[2] = 10;
}
Without using .splice you still have quite a few options. Let's look at a single, simple example for now.
This solution involves using a reverse loop, and adjusting our indices manually to insert a single value. This alters the original array.
You can see here we are just shifting our next values into our current position, which starts at the index that is equivalent to our length. Then we can slot our new value in the final index.
function insert (array, index, value) {
var i = array.length;
while (i > index) {
array[i] = array[--i]; // Prefix is important.
}
array[i] = value; // Or array[index], i === index at this point.
return array;
}
console.log(insert([1,2,3,4], 2, 'B'));
This only inserts a single element into the array. Can you figure out how to insert multiple elements starting from the index?
If you are allowed to use .slice and .concat you can emulate an inserting .splice. This returns a new array.
function insert (array, index, value) {
return array.slice(0, index).concat(value, array.slice(index));
}
console.log(insert([1,2,3,4], 2, 'B'));
Since inserting affects the length of the array, one approach is to start moving elements from the end to their current position + 1, then when the required index is reached, insert the new element.
Using a decrementing for loop:
function slowSplice(array, element, index) {
for (var i=array.length; i>index; i--) {
array[i] = array[i-1];
}
array[index] = element;
}
Using a decrementing while loop:
function slowSplice2(array, element, index) {
var i = array.length;
while (i > index) {
array[i] = array[--i];
}
array[index] = element;
}
Note that the above assumes a contiguous array. If sparse arrays must be accommodated, more work is required. If the function is required to be implemented as a general function, the splice algorithm from ECMA-262 should be followed.

Categories