I have an ASCII character string and I need to convert it to a normal string.
var asciiString = '84114117116104326510811997121115328710511011532'
function strFunc(str) {
var result = []
var strSplit = str.split('');
var validAscii = ['32'];
for(var i=65; i<=90; i++) {
validAscii.push(i.toString());
}
for(var i=97; i<=122; i++) {
validAscii.push(i.toString());
}
strSplit.forEach((item, index) => {
if(validAscii.includes(parseInt(item))) {
result.push(item)
} else if (validAscii.includes(`${parseInt(strSplit[index])}${parseInt(strSplit[index + 1])}`)){
result.push(item)
}
})
return result.fromCharCodeAt(...result)
}
console.log(strFunc(asciiString))
Why does it return an empty string? I need to split the string into either 2 digit or 3 digits and compare it with the array I built.
The string should be split as [84, 114, 117, 116, 104, 32, 65, ...] which translates to TRUTH A....
Please advice.
I'd do this way
const encodedString = '84114117116104326510811997121115328710511011532';
const codes = [];
for (let i = 0; i < encodedString.length;) {
const numDigits = encodedString[i] === '1' ? 3 : 2;
codes.push(encodedString.substr(i, numDigits));
i += numDigits;
}
const str = String.fromCharCode(...codes);
console.log(`"${str}"`);
Some notes:
It assume values in the encoded string go from 32 to 127. There's no error checking
There's no reason to call parseInt as JavaScript will convert numbers strings to numbers so passing the numbers as strings to String.fromCharCode works.
As for why your code doesn't work, a couple of issues include
it's looping over every character, not every code.
It's looping over 8, 4, 1, 1, 4, ... instead of 84, 114, ...
This means neither test will pass since item will never be something found in validAscii which means result will have nothing pushed to it.
There's no function Array.fromCharCodeAt
result is an array and there is no such function as array.fromCharCodeAt. If result had the correct codes in it then you could use String.fromCharCode(...result)
When you're combining two elements of the string, you need to call parseInt() *on the result of the concatenation, not concatenate the results of parseInt(). So it should be:
} else if (validAscii.includes(parseInt(item + strSplit[index+1]))){
And since ASCII values can be 3 digits, you need another else if that looks for item + strSplit[index+1] + strSplit[index+2].
Another problem is that you're pushing item onto the result string. But to get the corresponding character, you need to use String.fromCharCode() to convert the concatenated ASCII code to a character.
strSplit.forEach((item, index) => {
if (validAscii.includes(parseInt(item))) {
result.push(String.fromCharCode(item))
} else if (validAscii.includes(parseInt(item + strSplit[index+1]))) {
result.push(String.fromCharCode(parseInt(item + strSplit[index+1]))
} else if (validAscii.includes(parseInt(item + strSplit[index+1] + strSplit[index+2]))) {
result.push(String.fromCharCode(parseInt(item + strSplit[index+1] + strSplit[index+2]))
}
})
Note that using forEach like this is probably not a good idea. If there are overlapping items in the input that are both in validAscii, you'll add both of them to the result. E.g. if it contains 678 you'll match both 67 and 78, and add the corresponding characters to the result. Instead, you should use an ordinaryfor` loop, and increment the index by the number of characters that you matched.
You want parseInt around the templated string, not the individual items. You were checking if the string is included in an array filled with numbers.
var asciiString = '84114117116104326510811997121115328710511011532'
function strFunc(str) {
var result = []
var strSplit = str.split('');
var validAscii = [32];
for(var i=65; i<=90; i++) {
validAscii.push(i);
}
for(var i=97; i<=122; i++) {
validAscii.push(i);
}
strSplit.forEach((item, index) => {
if(validAscii.includes(parseInt(item))) {
result.push(item)
} else if (validAscii.includes(parseInt(`${(strSplit[index])}${(strSplit[index + 1])}`))){
result.push(item);
}
})
return result.join('');
}
console.log(strFunc(asciiString))
Related
I have the following problem statement:
Write a function, uncompress, that takes in a string as an argument.
The input string will be formatted into multiple groups according to
the following pattern:
number + char
for example, '2c' or '3a'.
The function should return an uncompressed version of the string where
each 'char' of a group is repeated 'number' times consecutively. You
may assume that the input string is well-formed according to the
previously mentioned pattern.
test_00: uncompress("2c3a1t"); // -> 'ccaaat'
Here is my code which is using a stack. The problem is that it's only returning 'cc' and I can't figure out why. I've console logged what goes into the IF ELSE and I'm hitting both so I don't understand why nothing gets pushed to the stack.
Would really appreciate the help if someone can spot what I'm missing.
const uncompress = (s) => {
const nums = '23456789';
const stack = [];
for (let char of s) {
if (nums.includes(char)) {
stack.push(Number(char));
} else {
const num = stack.pop();
stack.push(char.repeat(num));
};
};
return stack.join('');
};
console.log(uncompress("2c3a1t")); // -> 'ccaaat'
Here's how I would do it:
Split the string up into pairs of numbers and chars:
str.match(/\d+[a-zA-Z]/g)
And reduce that array to a string, while taking each value from the array, getting the char from it (cv.match(/[a-zA-Z]/)[0]) and repeating it according to the number (.repeat(parseInt(cv)))
const uncompress = str => str.match(/\d+[a-zA-Z]/g).reduce((acc, cv) =>
acc + cv.match(/[a-zA-Z]/)[0].repeat(parseInt(cv)), "")
console.log(uncompress("2c3a1t"))
console.log(uncompress("27b1d8g"))
And just like that I was able to write the code which passed the test case:
const nums = '123456789';
const stack = [];
for (let char of s) {
if (nums.includes(char)) {
stack.push(Number(char));
} else {
let num = '';
while (nums.includes(stack[stack.length - 1])) {
num += stack.pop();
}
stack.push(char.repeat(num));
};
};
return stack.join('');
};
I am writing a HTML/JS bingo game. It needs to have functionality that lets users specify their own bingo board by entering a string that needs to be validated using a regex. The specifications are as follows:
The string format will be
B(15,9,8,7,14)I(25,21,20,22,29)N(38,41,f,34,31)G(60,57,48,56,49)O(69,70,72,64,71)
where B(15,9,8,7,14) means that the B column on the board contains
15, 9, 8, 7, and 14. I(25,21,20,22,29) means the I column contains
25, 21, 20, 22 and 29. And so forth. 'f' is used in the string to
represent the free space.
So far I have:
var string = /(B|b)(((1[0-5]|[1-9]),?){5})(I|i)(((1[6-9]|2[0-9]|30),?){5})(N|n)(((3[1-9]|4[0-5]),){2}(F|f),((3[1-9]|4[0-5]),?){2})(G|g)(((4[6-9]|5[0-9]|60),?){5})(O|o)(((6[1-9]|7[0-5]),?){5})/g;
Which validates the above example but doesn't check for duplicates. I.e., B(15,15,8,7,14)... should fail. I think negative lookahead is the right tool to use but i'm unsure how to use it in this context
Extract numbers from string into an Array, and check for duplicates:
var bingoStrings = [
"B(15,9,8,7,14)I(25,21,20,22,29)N(38,41,f,34,31)G(60,57,48,56,49)O(69,70,72,64,71)",
"B(15,15,8,7,14)I(25,21,20,22,29)N(38,41,f,34,31)G(60,57,48,56,49)O(69,70,72,64,71)"
];
bingoStrings.forEach(bingoString => {
var bingoArray = bingoString.match(/\d+/g);
var hasDuplicates = bingoArray.some(number =>
bingoArray.indexOf(number) !== bingoArray.lastIndexOf(number)
);
console.log(bingoString);
console.log("has " + (hasDuplicates?"":"no ") + "duplicates\n");
});
As a function:
var bingoStrings = [
"B(15,9,8,7,14)I(25,21,20,22,29)N(38,41,f,34,31)G(60,57,48,56,49)O(69,70,72,64,71)",
"B(15,15,8,7,14)I(25,21,20,22,29)N(38,41,f,34,31)G(60,57,48,56,49)O(69,70,72,64,71)"
];
hasDuplicates = (bingoString) =>
bingoString
.match(/\d+/g)
.some((number, index, bingoArray) =>
bingoArray.indexOf(number) !== bingoArray.lastIndexOf(number)
)
;
bingoStrings.forEach(bingoString => {
console.log(bingoString);
console.log("has " + (hasDuplicates(bingoString)?"":"no ") + "duplicates\n");
});
Suppose that I've got a node.js application that receives input in a weird format: strings with JSON arbitrarily sprinkled into them, like so:
This is a string {"with":"json","in":"it"} followed by more text {"and":{"some":["more","json"]}} and more text
I have a couple guarantees about this input text:
The bits of literal text in between the JSON objects are always free from curly braces.
The top level JSON objects shoved into the text are always object literals, never arrays.
My goal is to split this into an array, with the literal text left alone and the JSON parsed out, like this:
[
"This is a string ",
{"with":"json","in":"it"},
" followed by more text ",
{"and":{"some":["more","json"]}},
" and more text"
]
So far I've written a naive solution that simply counts curly braces to decide where the JSON starts and stops. But this wouldn't work if the JSON contains strings with curly braces in them {"like":"this one } right here"}. I could try to get around that by doing similar quote counting math, but then I also have to account for escaped quotes. At that point it feels like I'm redoing way too much of JSON.parse's job. Is there a better way to solve this problem?
You can check if JSON.parse throws an error to determine if the chunk is a valid JSON object or not. If it throws an error then the unquoted } are unbalanced:
const tests = [
'{"just":"json }}{}{}{{[]}}}}","x":[1,2,3]}',
'Just a string',
'This string has a tricky case: {"like":"this one } right here"}',
'This string {} has a tiny JSON object in it.',
'.{}.',
'This is a string {"with":"json","in":"it"} followed by more text {"and":{"some":["more","json"]}} and more text',
];
tests.forEach( test => console.log( parse_json_interleaved_string( test ) ) );
function parse_json_interleaved_string ( str ) {
const chunks = [ ];
let last_json_end_index = -1;
let json_index = str.indexOf( '{', last_json_end_index + 1 );
for ( ; json_index !== -1; json_index = str.indexOf( '{', last_json_end_index + 1 ) ) {
// Push the plain string before the JSON
if ( json_index !== last_json_end_index + 1 )
chunks.push( str.substring( last_json_end_index, json_index ) );
let json_end_index = str.indexOf( '}', json_index + 1 );
// Find the end of the JSON
while ( true ) {
try {
JSON.parse( str.substring( json_index, json_end_index + 1 ) );
break;
} catch ( e ) {
json_end_index = str.indexOf( '}', json_end_index + 1 );
if ( json_end_index === -1 )
throw new Error( 'Unterminated JSON object in string' );
}
}
// Push JSON
chunks.push( str.substring( json_index, json_end_index + 1 ) );
last_json_end_index = json_end_index + 1;
}
// Push final plain string if any
if ( last_json_end_index === - 1 )
chunks.push( str );
else if ( str.length !== last_json_end_index )
chunks.push( str.substr( last_json_end_index ) );
return chunks;
}
Here's a comparatively simple brute-force approach: split the whole input string on curly braces, then step through the array in order. Whenever you come across an open brace, find the longest chunk of the array from that starting point that successfully parses as JSON. Rinse and repeat.
This will not work if the input contains invalid JSON and/or unbalanced braces (see the last two test cases below.)
const tryJSON = input => {
try {
return JSON.parse(input);
} catch (e) {
return false;
}
}
const parse = input => {
let output = [];
let chunks = input.split(/([{}])/);
for (let i = 0; i < chunks.length; i++) {
if (chunks[i] === '{') {
// found some possible JSON; start at the last } and backtrack until it works.
for (let j = chunks.lastIndexOf('}'); j > i; j--) {
if (chunks[j] === '}') {
// Does it blend?
let parsed = tryJSON(chunks.slice(i, j + 1).join(""))
if (parsed) {
// it does! Grab the whole thing and skip ahead
output.push(parsed);
i = j;
}
}
}
} else if (chunks[i]) {
// neither JSON nor empty
output.push(chunks[i])
}
}
console.log(output)
return output
}
parse(`{"foo": "bar"}`)
parse(`test{"foo": "b}ar{{[[[{}}}}{}{}}"}`)
parse(`this {"is": "a st}ri{ng"} with {"json": ["in", "i{t"]}`)
parse(`{}`)
parse(`this {"i{s": invalid}`)
parse(`So is {this: "one"}`)
I could try to get around that by doing similar quote counting math, but then I also have to account for escaped quotes. At that point it feels like I'm redoing way too much of JSON.parse's job. Is there a better way to solve this problem?
I don't think so. Your input is pretty far from JSON.
But accounting for all those things isn't that hard.
The following snippet should work:
function construct(str) {
const len = str.length
let lastSavedIndex = -1
let bracketLevel = 0
let inJsonString = false
let lastCharWasEscapeChar = false
let result = []
for(let i = 0; i < len; ++i) {
if(bracketLevel !== 0 && !lastCharWasEscapeChar && str[i] === '"') {
inJsonString = !inJsonString
}
else if (!inJsonString && str[i] === '{') {
if (bracketLevel === 0) {
result.push(str.substring(lastSavedIndex + 1, i))
lastSavedIndex = i - 1
}
++bracketLevel
}
else if (!inJsonString && str[i] === '}') {
--bracketLevel
if (bracketLevel === 0) {
result.push(JSON.parse(str.substring(lastSavedIndex + 1, i + 1)))
lastSavedIndex = i
}
}
else if (inJsonString && str[i] === '\\') {
lastCharWasEscapeChar = !lastCharWasEscapeChar
}
else {
lastCharWasEscapeChar = false
}
}
if(lastSavedIndex !== len -1) {
result.push(str.substring(lastSavedIndex + 1, len))
}
return result
}
const standardText = 'This is a string {"with":"json","in":"it"} followed by more text {"and":{"some":["more","json"]}} and more text. {"foo": "bar}"}'
const inputTA = document.getElementById('input')
const outputDiv = document.getElementById('output')
function updateOutput() {
outputDiv.innerText =
JSON.stringify(
construct(inputTA.value),
null,
2
)
}
inputTA.oninput = updateOutput
inputTA.value = standardText
updateOutput()
<textarea id="input" rows="5" cols="50"></textarea>
<pre id="output"><pre>
You can use RegExp /(\s(?=[{]))|\s(?=[\w\s]+[{])/ig to .split() space character followed by opening curly brace { or space character followed by one or more word or space characters followed by opening curly brace, .filter() to remove undefined values from resulting array, create a new array, then while the resulting split array has .length get the index where the value contains only space characters, .splice() the beginning of the matched array to the index plus 1, if array .length is 0 .push() empty string '' else space character ' ' with match .join()ed by space character ' ' .replace() last space character and .shift() matched array, which is JSON, then next element of the matched array.
const str = `This is a string {"with":"json","in":"it"} followed by more text {"and":{"some":["more","json"]}} and more text {"like":"this one } right here"}`;
const formatStringContainingJSON = s => {
const r = /(\s(?=[{]))|\s(?=[\w\s]+[{])/ig;
const matches = s.split(r).filter(Boolean);
const res = [];
while (matches.length) {
const index = matches.findIndex(s => /^\s+$/.test(s));
const match = matches.splice(0, index + 1);
res.push(
`${!res.length ? '' : ' '}${match.join(' ').replace(/\s$/, '')}`
, `${matches.shift()}`
);
};
return res;
}
let result = formatStringContainingJSON(str);
console.log(result);
Here you one approach that iterates char by char. First we create an array from the input and then use reduce() on it. When we detect an opening curly bracket { we push the current accumulated chunk on an array of detected results, and then we set a flag on the accumulator object we are using on reduce. While this flag is set to true we will try to parse for a JSON and only when success we put the chunk representing the JSON on the array of detected results and set the flag again to false.
The accumulator of the reduce() method will hold next data:
res: an array with detected results: strings or jsons.
chunk: a string representing the current accumulated chunk of chars.
isJson: a boolean indicating if the current chunk is json or not.
const input = 'This is a string {"with":"json", "in":"it"} followed by more text {"and":{"some":["more","json","data"]}} and more text';
let obj = Array.from(input).reduce(({res, isJson, chunk}, curr) =>
{
if (curr === "{")
{
if (!isJson) res.push(chunk);
chunk = isJson ? chunk + curr : curr;
isJson = true;
}
else if (isJson)
{
try
{
chunk += curr;
JSON.parse(chunk);
// If no error, we found a JSON.
res.push(chunk);
chunk = "";
isJson = false;
}
catch(e) {/* Ignore error */}
}
else
{
chunk += curr;
}
return {res, isJson, chunk};
}, {res:[], isJson:false, chunk:""})
// First stage done, lets debug obtained data.
obj.res.push(obj.chunk);
console.log(obj.res);
// Finally, we map the pieces.
let res = obj.res.map(x => x.match("{") ? JSON.parse(x) : x);
console.log(res);
Obligatory answer: this is an improper format (because of this complication, and the guarantee is a security hole if the parser is improperly designed); it should ideally be redesigned. (Sorry, it had to be said.)
Barring that, you can generate a parser using your favorite parser generator that outputs to javascript as a target language. It might even have a demo grammar for JSON.
However, the glaring security issue is incredibly scary (if any JSON gets past the 'guarantee', suddenly it's a vector). An array interspersed representation seems nicer, with the constraint that assert(text.length == markup.length+1):
'{
"text": ['Hello', 'this is red text!'],
"markup": [{"text":"everyone", "color":"red"}]
}'
or even nicer:
'[
{"type":"text", "text":"Hello"},
{"type":"markup", "text":"everyone", "color":"red"} # or ,"val":{"text":.., "color":..}}
{"type":"text", "text":"this is red text!"},
...
]'
Store compressed ideally. Unserialize without any worries with JSON.parse.
I'm working on a code puzzle which uses the following script:
function(ascii,a,b,c) {
for(i=0;i<ascii.length;i++) {
if(i%3==0){ascii[i]=(ascii[i]+a)%256;}
if(i%3==1){ascii[i]=(ascii[i]+b)%256;}
if(i%3==2){ascii[i]=(ascii[i]+c)%256;}
}
return ascii;
}
I think it says: ' for each character in the message (or if the message is an array, for each element i in the array), calculate its position mod3, and depending on the result add either a, b or c to the ascii value for the character, and then return that value mod 256. Is this the correct interpretation?
It seems to be an encoding function which takes an array of numbers representing characters (0-255). Then shifts the characters by the length specified as per a,b, and c by modding by 3. Then the output is modded by 255 so it remains a valid character value. Finally the resulting encoding array of numbers representing characters is returned.
Below is the code:
function encodeIt(ascii,a,b,c) {
for(i=0;i<ascii.length;i++) {
if(i%3==0){ascii[i]=(ascii[i]+a)%256;}
if(i%3==1){ascii[i]=(ascii[i]+b)%256;}
if(i%3==2){ascii[i]=(ascii[i]+c)%256;}
}
return ascii;
}
function encodeString(myString,myAdjustments)
{
var myArray = [];
for(var i=0;i<myString.length;i++)
{
myArray.push(myString.charCodeAt(i));
}
var myArray = encodeIt(myArray,myAdjustments[0],myAdjustments[1],myAdjustments[2]);
var myEncodedString = "";
for(var i=0;i<myArray.length;i++)
{
myEncodedString+=String.fromCharCode(myArray[i]);
}
return myEncodedString;
}
var encodedString = encodeString("Hello World!",[30,80,-13]);
alert(encodedString);
var decodedString = encodeString(encodedString,[-30,-80,13]);
alert(decodedString);
And the fiddle:
https://jsfiddle.net/3p79bxv2/
Lets say I had a variable called test and test = 123456789;. Then I have another variable called anotherTest and anotherTest = 1234;. How would I make a program that can test whether a variable has the digit 5 or not? Then, how could it sort the variables into two groups of which one group of variables has the digit "5" within it and the other without? Is there a easy way to do this?
How would I make a program that can test whether a variable has the digit 5 or not?
You can readily do that with strings and indexOf:
if (String(test).indexOf("5") !== -1) {
// It has a 5 in it
}
Then, how could it sort the variables into two groups of which one group of variables has the digit "5" within it and the other without?
You can't sort the variables into groups, but you can certainly sort values into groups. For example, this loops through an array and adds values to either the with5 or without5 array depending on whether the value contains the digit 5:
var a = [
1234,
12345,
123123,
555555
];
var with5 = [];
var without5 = [];
a.forEach(function(value) {
if (String(value).indexOf("5") === -1) {
without5.push(value);
} else {
with5.push(value);
}
});
snippet.log("with5: " + with5.join(", "));
snippet.log("without5: " + without5.join(", "));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
The above assumes base 10 (decimal) strings, but you can easily do the same with hexadecimal or octal or any other base you like by using Number#toString(base). E.g.:
var s = num.toString(16);
...will assign s the value of num as a hexadecimal (base 16) string.
Loop through each character of variable test, then compare using indexOf() to see if it exists in anotherTest. If so add to one array, otherwise add to array 2.
To see if a number contains the digit "5", you can just convert the numbers to strings and then just use .indexOf("5") on each string.
var test = 123456789;
var anotherTest = 1234;
// reports whether the passed in number or string contains the
// character "5"
function containsDigit5(val) {
// convert number to string
// if already string, then it is left as is
val = "" + val;
return val.indexOf("5") >= 0;
}
containsDigit5(test); // true
containsDigit5(anotherTest); // false
The grouping part of your question is not entirely clear, but you can just call this function on each variable and add the numbers to one of two arrays.
var testNumbers = [123456789, 1234];
var has5 = [];
var doesNotHave5 = [];
// reports whether the passed in number or string contains the
// character "5"
function containsDigit5(val) {
// convert number to string
// if already string, then it is left as is
val = "" + val;
return val.indexOf("5") >= 0;
}
testNumbers.forEach(function(item) {
if (containsDigit5(item)) {
has5.push(testNumbers[i]);
} else {
doesNotHave5.push(testNumbers[i]);
}
});
You can do this with RegExp, or .indexOf. Either works:
RegEx
Everyone hates RegExp for some reason, but I like it. You can use:
var test = 123456789,
anotherTest = 1234;
/5/.test(test);
/5/.test(anotherTest);
var test = 123456789,
anotherTest = 1234;
document.write( 'test (123456789): ' + /5/.test(test) + '<br/>' );
document.write( 'anotherTest (1234): ' + /5/.test(anotherTest) );
indexOf
This can be faster in some situations, but not always, it is also a bit more "complicated", at least in my opinion:
var test = 123456789,
anotherTest = 1234;
(test+'').indexOf(5) > -1;
(anotherTest+'').indexOf(5) > -1;
var test = 123456789,
anotherTest = 1234;
document.write( 'test (123456789): ' + ((test+'').indexOf(5) > -1) + '<br/>' );
document.write( 'anotherTest (1234): ' + ((anotherTest+'').indexOf(5) > -1) + '<br/>' );