1

Given the name of a class (CustomClass6) and a string that represents an array with ANY format, it should append the name of the class to the array keeping the format.

Example 1:

Given this string:

"""
const arr = [
  CustomClass11,
  CustomClass22,
  CustomClass33,
  CustomClass44,
  CustomClass55
];
"""

The function should return:

"""
const arr = [
  CustomClass11,
  CustomClass22,
  CustomClass33,
  CustomClass44,
  CustomClass55,
  CustomClass6
];
"""

Example 2:

Given the string:

"""
const arr = [CustomClass1, CustomClass2, CustomClass3];
"""

It should return:

"""
const arr = [CustomClass1, CustomClass2, CustomClass3, CustomClass6];
"""

Example 3:

"""
const arr = [];
"""

It should return:

"""
const arr = [CustomClass6];
"""

These are the functions I have made this far. I have been able to solve the example 1, but as you can see I have hardcoded the separator to be \n:


function getStringWithElementAppended(
  stringWithArray: string,
  classToAppend: string
) {
  const openingBraketIndex = stringWithArray.indexOf('[');
  const closingBracketIndex = getIndexInString(
    stringWithArray,
    ']',
    openingBraketIndex
  );

  const updatedContent = appendElementToArrayInString(
    classToAppend,
    openingBraketIndex,
    closingBracketIndex,
    stringWithArray
  );

  return updatedContent;
}

function getIndexInString(
  String: string,
  stringToLookFor: string,
  startingIndex: number = 0,
  limitIndex: number = String.length,
  lastIndex = false
) {
  const auxString = String.slice(startingIndex, limitIndex);

  if (lastIndex) {
    return startingIndex + auxString.lastIndexOf(stringToLookFor);
  } else {
    return startingIndex + auxString.indexOf(stringToLookFor);
  }
}

function appendElementToArrayInString(
  elementToAppend: string,
  arrayBeginingIndex: number,
  arrayEndIndex: number,
  String: string
) {
  const separationBetweenElements = '\n';
  const endOfArrayIndex = getIndexInString(
    String,
    separationBetweenElements,
    arrayBeginingIndex,
    arrayEndIndex,
    true
  );
  const previousElementEndIndex = getIndexInString(
    String,
    separationBetweenElements,
    arrayBeginingIndex,
    endOfArrayIndex,
    true
  );
  const spaceBetweenElements = String.slice(
    previousElementEndIndex + 1,
    endOfArrayIndex
  );
  const whitespaceIndex = spaceBetweenElements.lastIndexOf(' ');
  const whitespace = spaceBetweenElements.slice(0, whitespaceIndex + 1);
  const contentToAdd =
    ',' +
    separationBetweenElements +
    whitespace +
    elementToAppend +
    separationBetweenElements;
  const updatedString =
    String.slice(0, endOfArrayIndex) + contentToAdd + String.slice(arrayEndIndex);
  return updatedString;
}

At the end of the day, the output should respect the formatting of the input. I am using this code in a script that modifies existing files, and we don't want our SCM to detect a lot of formatting changes.

Leandro
  • 1,244
  • 16
  • 28
  • please add the given data as code to the question and the wanted result. – Nina Scholz Sep 11 '19 at 19:03
  • Wouldn't it be easier to turn the string into an array object, push the new value into the array and then stringify the array for whatever you need the string for? `let arrObj = JSON.parse(stringArray);` then `arrObj.push(newValue);` then `stringArray = JSON.stringify(arrObj);` – daddygames Sep 11 '19 at 19:05
  • 1
    he cant because there are no variables `CustomClass34` because its not `"CustomClass34"` – joyBlanks Sep 11 '19 at 19:07
  • @NinaScholz What exactly am I missing? I think the wanted result is there. Could you be more specific about what part I can clarify more? – Leandro Sep 11 '19 at 19:12
  • i am still wondering, what kind of string you have, and want. – Nina Scholz Sep 11 '19 at 19:12
  • @daddygames Besides what joyBlanks said, my problem is not "pushing" to the array, but keeping the format – Leandro Sep 11 '19 at 19:13
  • Maybe use `lastIndexOf` on the string to find the last bracket, then inject the value into the string right before that index while also adding the comma. https://stackoverflow.com/questions/4313841/insert-a-string-at-a-specific-index – daddygames Sep 11 '19 at 19:15
  • do you even need add if doesn't exist? – joyBlanks Sep 11 '19 at 19:16
  • @daddygames But what if there is a linebreak between the two elements? – Leandro Sep 11 '19 at 19:19
  • @joyBlanks Sorry Joy, I did'n understand the question – Leandro Sep 11 '19 at 19:19
  • @Leandro why would it matter if there is a line-break in the string? Those can be added to the string also using `\n` if that's part of the formatting concern. – daddygames Sep 11 '19 at 19:21
  • @daddygames There will be cases where the linebreak is not needed, and some cases where it will be needed. It will depend on the current formatting of the array. What I need is the algorithm to discern between those two cases (plus when the array is empty, or with many elements but without whitespace). – Leandro Sep 11 '19 at 19:23
  • @NinaScholz Edited. Do you think it is clear enough now? – Leandro Sep 11 '19 at 19:24
  • You can detect if line breaks exist in a string -> https://stackoverflow.com/questions/21711768/split-string-in-javascript-and-detect-line-break – daddygames Sep 11 '19 at 19:25
  • i have still no idea. and btw, i would change the data representation to a common format, like JSON, which is parsable and easily maintainable. – Nina Scholz Sep 11 '19 at 19:26
  • In fact it is an Angular's Schematics project, and I am parsing an array in an actual json. I just posted the simplest problem of the system, but I can see why it is not clear enough. I am going to edit it again – Leandro Sep 11 '19 at 19:28
  • the right question is what exactly are you trying to do, can we have a little bit of a background here? How did you end up with what you have that string which contains an arr variable and the array. Maybe we can suggest alternatives rather than providing a solution to a unpredictable problem – joyBlanks Sep 11 '19 at 20:03

2 Answers2

1

The main complication is the separator. There is no built in way to do this. In order to detect the separator, you need to essentially parse the string as a grammar.

The grammar will include:

  • variable declaration
  • array syntax
  • prefix padding
  • values
  • separators
  • suffix padding

Our language we develop will be based on the construction of this grammar, where it is essentially an easy production of each piece. The separator will need to be detected based on the value set.

// Test data

const inputa = 
`"""
const arr = [
  CustomClass25,
  CustomClass9,
  CustomClass12,
  CustomClass8
];
"""`;

const inputb = 
`"""
const arr = [CustomClass1, CustomClass2, CustomClass3];
"""`;

const inputc = 
`"""
const arr = [];
"""`;

// Main actor
function modifyDeclaration(declaration,addition){

 // Retain starting declaration index
 const startingSyntax = declaration.indexOf('[');

 // Retain ending declaration index
 const endingSyntax = declaration.indexOf('];');

 // Obtain the contained array from syntax
 const arrayOnly = declaration.substring(startingSyntax+1,endingSyntax);

 // Determine the array's prefix padding
 const prefixPadding = arrayOnly.substring(0,arrayOnly.search(/\S|$/));

 // Determine the array's suffix padding
 // by reversing the string and finding
 // the first nonwhitespace character
 const suffixPadding = arrayOnly.substring(arrayOnly.length - arrayOnly.split('').reverse().findIndex(s => /\S/.test(s)));

 // Strip array padding for value insertion
 const values = arrayOnly.substring(prefixPadding.length,arrayOnly.length - suffixPadding.length);

 // Assume base separation is the same
 // as the trailing padding in cases
 // of no set
 let separator = suffixPadding;

 // Gather the index of the first comma for
 // separator detection
 const commaIndex = arrayOnly.indexOf(',');

 // When there are values or a commas
 if(commaIndex > -1 || values.length > 0){

  // Make the separator include a comma
  // as well as the first separator
  separator = ',' + arrayOnly.substring(
    commaIndex+1,
    commaIndex+1 + arrayOnly.substring(commaIndex+1).search(/\S|$/)
  );
 }

 // Return our production value
 return declaration.substring(0,startingSyntax+1) + 
   prefixPadding +
   values +
   separator +
   addition +
   suffixPadding + 
   declaration.substring(endingSyntax)
}

const addition = 'CustomClass42';

console.log(modifyDeclaration(inputa,addition));
console.log(modifyDeclaration(inputb,addition));
console.log(modifyDeclaration(inputc,addition));
Travis J
  • 81,153
  • 41
  • 202
  • 273
  • Travis, I haven't tried it, but I can see that it would not work if the original array was something like this: ``` """ const arr = []; """ ``` As it would end up like this: ``` """ const arr = [ CustomClass2 ]; """ ``` Right? – Leandro Sep 11 '19 at 19:29
  • 1
    Wrap your code in comments with `` in order for it to render. – Travis J Sep 11 '19 at 19:29
  • @Leandro - That is true. I am sure there are many edge cases that are problematic, however, it is impossible to solve for them unless you describe the situation in more detail. At present, this represents a solution to your described problem. If you would perhaps give a more real representation of what you are actually working with, it would probably benefit everyone. – Travis J Sep 11 '19 at 19:31
  • Shouldn't it be rendered with three times `? – Leandro Sep 11 '19 at 19:31
  • Seems fair. I will write the question again. Thank you – Leandro Sep 11 '19 at 19:31
  • No, just one in comments. The three is for posts which will indicate a code block. In posts though, you can also simply use four spaces of left padding for a code block (for multi line, each line needs to have the padding). – Travis J Sep 11 '19 at 19:32
  • @Leandro - Does the formatting of the array need to be so readable? Can it simply be `const arr = [1,2,3]`? Or is that not going to work for you? – Travis J Sep 11 '19 at 19:35
  • It needs to be the same in the output as in the input. I am using this code in a script that modifies existing files, and we don't want our SCM to detect a lot of formatting changes. I have edited the question. Do you think it is more clear now Travis? What could I clarify more? – Leandro Sep 11 '19 at 19:39
  • @Leandro - That is more specific. It does seem like there is no general solution to detecting format though. That is probably just going to require a rather custom implementation. One clarification you could make would be the overall domain of what "CustomClass" could actually contain. If it can contain a JavaScript object definition for example, that would definitely complicate things. Also, is CustomClass12 intended to reference actual variable names, since it is not wrapped in quotes? – Travis J Sep 11 '19 at 19:47
  • The elements in the array will always be classes, good observation. This is also true for the element I want to append. – Leandro Sep 11 '19 at 19:49
  • @Leandro - I had other things to do, but came back to this since it seemed interesting. See edits. – Travis J Sep 11 '19 at 22:57
  • 1
    You are a genius Travis J! Thank you very much! – Leandro Sep 12 '19 at 14:31
0

I am not sure whether the following will help you, still give it a try. The only issue you might face is having duplicate entries in the array, if you push it when it already exists.

let data = `"""
const arr = [CustomClass1, CustomClass2, CustomClass3];
"""`;
let newData = "CustomClass6";

let InsertionPointType1 = data.match(/(\n)+( )*]( )*;/g)?data.match(/(\n)+( )*]( )*;/g)[0]:null;
//The InsertionPointType1 is
//
//];
if (InsertionPointType1)
  data = data.replace(InsertionPointType1, ",\n  " + newData + InsertionPointType1);
else {
  InsertionPointType2 = data.match(/]( )*;/g)[0];
  if (InsertionPointType2)
    //The InsertionPointType2 is
    //];
    data = data.replace(InsertionPointType2, ", " + newData + InsertionPointType2);
  else
    throw new Error("Invalid nput Format");
}
//error correction incase of empty array
let errorPoint = data.match(/\[[ ,\n]*\]( )*;/g)?data.match(/\[[ ,\n]*\]( )*;/g)[0]:null;
//[
//,
// OR
//[,
if(errorPoint)
data=data.replace(errorPoint,"[");

console.log(data);
Rohith K N
  • 845
  • 6
  • 17
  • Rohith, as you are hardcoding the `\n` it wouldn't work in examples 2 and 3 (see the question, it was edited after you posted your answer). – Leandro Sep 11 '19 at 19:45