javascriptgoogle-sheetstextstyle

Apply text syle to a regex pattern in google sheet cells


I would like to get parts of strings (by regex) into a specific text style, but I can't manage the loop. I always get errors.

In the first row is the original text (strings separated by commas), and in the second under, is the desired text style. enter image description here

Here is the sheet (french parameters) https://docs.google.com/spreadsheets/d/1vq0Ai_wEr3MamEQ-kMsXW7ZGg3RxrrkE5lITcYjO-rU/edit?usp=sharing

function NomsStyleBotanique(){
  const classeur = SpreadsheetApp.getActive();  // var Feuille = classeur.getSheetByName('Feuille 1'); 
  var ligne = classeur.getCurrentCell().getRow();
  var colonne = classeur.getCurrentCell().getColumn();
  var range = classeur.getActiveRange();

  var richTextValues = range.getRichTextValues().map(([a]) => {
  var text = a.getText();
  var pos = 0;
  
  var myregEx = /,/g;
  var Noms = text.split(myregEx);
  
  var textStyleNomPlante = SpreadsheetApp.newTextStyle()
        .setFontSize(10)
        .setForegroundColor("black")
        .setFontFamily("Times New Roman")
        .setItalic(false)
        .build();

    var textStyleNomAuteur = SpreadsheetApp.newTextStyle()
        .setFontSize(10)
        .setForegroundColor("#616399")     // ("grey")
        .setFontFamily("Times New Roman")
        .setItalic(true)
        .build();

  var nbPhrases = [];
  var i =0;
 
  while (Noms){ i++; nbPhrases.push(Noms[i]); // SpreadsheetApp.getUi().alert(Noms[i]); 
  
  for (var i=0;i<nbPhrases.length;i++){
  
  var myarr = Noms[i].split(" ");
  var Espace1 = myarr[0].length+1;
  var Espace2 = myarr[1].length+1;

  if (Noms[i]){
    if ((Noms[i].indexOf("subsp") > 1) || (Noms[i].indexOf("var.") > 1)){
    var Espace3 = myarr[2].length+1;
    var Espace4 = myarr[3].length+1;
    pos = Espace1+Espace2+Espace3+Espace4; }

  else {   pos = Espace1+Espace2;  } // pos = text.match(new RegExp(/\\s/, 'g'))[2]; 

  var position = pos;

  if (position > -1){
      var temp = a.getTextStyle(0, position - 1);

    return [
        SpreadsheetApp.newRichTextValue()
        .setText(Noms[i])
        .setTextStyle(0, position - 1, textStyleNomPlante)
        .setTextStyle(position, Noms[i].length, textStyleNomAuteur)
        .build()
      ];
    }
    return [SpreadsheetApp.newRichTextValue().setText(Noms[i]).setTextStyle(Noms[i].getTextStyle()).build()];
   }
  }
 }
} // fin boucle
);
range.setRichTextValues(richTextValues);
}

Solution

  • One problem here is that the author names are sometimes separated by a comma and sometimes separated by just a space. See Ten., Benth., Swart, and (Ten.) Kerguélen. However, in your comment, you said this does not happen often though and that you could just deal with this manually, so let's just assume for now that author names are never separated commas.

    With the assumption, we can split the contents of each cell by , and deal with each plant name/author separately:

    const plants = text.split(', ')
    
    for (const plant of plants) {
      // Find start/end of authors substring.
    }
    

    What we need is to find the indices where the "plant author" substring starts and ends.

    Finding the end index of the plant author substring is easy; it's just the end of the entire plant string:

    const end = plant.length
    

    To find the start of the plant author substring, we can look for the indices of the spaces ' '. (You'll need to write your own getIndices() method for this.) If the plant contains subsp. or var., the start index is the 4th space; otherwise, it is the 2nd space:

    let start
    spaceIndices = getIndices(plant, ' ')
    if (plant.includes('subsp.') || plant.includes('var.')) start = spaceIndices[3] + 1  // Add 1 to not include the space itself
    else start = spaceIndices[1] + 1  // Add 1 to not include the space itself
    

    Once we have the start/end indices, we can put them in an array offsets that we will use to find the startOffset and endOffset values for the setTextStyle() method.

    So now we have:

    const plants = text.split(', ')
    let offsets = []
    for (const plant of plants) {
      const end = plant.length
      
      let start
      spaceIndices = getIndices(plant, ' ')
      if (plant.includes('subsp.') || plant.includes('var.')) start = spaceIndices[3] + 1
      else start = spaceIndices[1] + 1
    
      offsets.push({
        start,
        end
      })
    }
    

    Next, we have to initiate the RichTextValueBuilder object and loop through the offsets array to determine what the startOffset and endOffset values should be for the setTextStyles() method by adding where start and end values we found earlier to index

    let richText = SpreadsheetApp.newRichTextValue()
      .setText(text)
    let authorTextStyle = SpreadsheetApp.newTextStyle()
      .setBold(true)
      .build()
    
    let plantStartIndex = 0
    for (const offset of offsets) {
      const startOffset = plantStartIndex + offset.start
      const endOffset = plantStartIndex + offset.end
      richText = richText.setTextStyle(startOffset, endOffset, authorTextStyle)
      plantStartIndex = plantStartIndex + offset.end + 2  // Add 2 to not include the ", " separator
    }
    

    Finally, build the RichTextValue object:

    richText = richText.build()
    

    …and tie it all together with the rest of your code:

    function stylePlantNames() {
    
      const ss = SpreadsheetApp.getActive()
      const range = ss.getActiveRange()
      const values = range.getValues()
    
      let richTextValues = []
    
      for (const row of values) {
        let text = row[0]
    
        const plants = text.split(', ')
    
        let offsets = []
        for (const plant of plants) {
          const end = plant.length
    
          let start
          spaceIndices = getIndices(plant, ' ')
          if (plant.includes('subsp.') || plant.includes('var.')) start = spaceIndices[3] + 1
          else start = spaceIndices[1] + 1
    
          offsets.push({
            start,
            end
          })
        }
    
        let richText = SpreadsheetApp.newRichTextValue()
          .setText(text)
        let authorTextStyle = SpreadsheetApp.newTextStyle()
          .setBold(true)
          .build()
    
        let plantStartIndex = 0
        for (const offset of offsets) {
          const startOffset = plantStartIndex + offset.start
          const endOffset = plantStartIndex + offset.end
          richText = richText.setTextStyle(startOffset, endOffset, authorTextStyle)
          plantStartIndex = plantStartIndex + offset.end + 2
        }
    
        richText = richText.build()
        richTextValues.push([richText])
    
      }
    
      range.setRichTextValues(richTextValues)
    
    }
    
    function getIndices(str, char) {
      let indices = [];
      for (var i = 0; i < str.length; i++) {
        if (str[i] === char) indices.push(i);
      }
      return indices;
    }
    

    I skipped over many details of how the Apps Script APIs work for spreadsheets and rich text formatting. You'll need to set your own styles, but from your code, it seems like you already know how to do this. The tricky part of your question is figuring out how to identify the author substring, so that's what I focused on for my answer.