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.
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);
}
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.