Adapting merge cells in InDesign script - javascript

I found this script on SO, which is very close to what I need. But instead of it merging the cells with the one above I need it to merge with the cell to left of any empty cell.
I have tried to experiment with it and manged to get it to merge horizontally once but it merge three cells that weren't empty into one.
Would be greatful for any help
THanks
var myDoc = app.activeDocument;
myPage = myDoc.pages;
for (var p=0; myPage.length>p; p++){
try{
var myTable = myPage[p].textFrames.everyItem().tables.everyItem();
if (myTable.constructor.name == 'Table'){
for (var t = myTable.cells.length - 1; t >= 0; t--)
{
if (myTable.cells[t].contents==""){
var w = myTable.columns.length;
myTable.cells[t-w].merge(myTable.cells[t]);
}
}
}
}
catch(e){}
}

I found a solution
var i, j, cells;
// Get all the rows in the document
var rows = app.documents[0].stories.everyItem().tables.everyItem().rows.everyItem().getElements();
for (i = 0; i < rows.length; i++) {
// Get all the cells in a row
cells = rows[i].cells.everyItem().getElements();
for (j = cells.length-1; j >= 1; j--) {
if (cells[j].contents == '') {
cells[j-1].merge (cells[j]);
}
}
}

Related

Non case sensitive remove row with specific data

I have this Google Apps Script that is checking specific columns for listed words in it and removes rows that match those words:
function sort() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var values = rows.getValues();
var rowsDeleted = 0;
var arrayOfWords = ['möbelübernahme','können übernommen werden','caravan','wohnwagen']
for (var i = values.length - 1; i >= 0; i--) {
var row = values[i];
for (var j = 0; j < arrayOfWords.length; j++) {
if (row['21','17'].indexOf(arrayOfWords[j]) > -1) {
sheet.deleteRow(i+1);
rowsDeleted++;
break;
}
}
}
};
Can I make the words check to be not case sensitive? So if I include a word like "mietwohnung" it will remove all matching options: MIETWOHNUNG, mietwohnung, Mietwohnung?
Explanation:
You need to convert each element in the row array to lower case. In order to do that, you need to use map() and toLowerCase(). In this way you will apply toLowerCase to every element of row. The modification will be here:
if (row.map(r=>r.toLowerCase()).indexOf(arrayOfWords[j]) > -1)
row['21','17'] is not a valid javascript object. You can't slice an array like that. If your goal is to check only column 21 and 17 then use this if statement instead:
if ([row[21],row[17]].map(r=>r.toLowerCase()).indexOf(arrayOfWords[j]) > -1)
Note here that row[21] is column V and row[17] is column R in the sheet. Remember the index in javascript starts from 0. Namely, 0 is column A, 1 is column B etc.
Solution:
function sort() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var values = rows.getValues();
var rowsDeleted = 0;
var arrayOfWords = ['möbelübernahme','können übernommen werden','caravan','wohnwagen']
for (var i = values.length - 1; i >= 0; i--) {
var row = values[i];
for (var j = 0; j < arrayOfWords.length; j++) {
if (row.map(r=>r.toLowerCase()).indexOf(arrayOfWords[j]) > -1) {
sheet.deleteRow(i+1);
rowsDeleted++;
break;
}
}
}
};
An alternative to the solution already provided
You can simply use toLowerCase() - in this way "Mietwohnung" will become "mietwohnung" for example, and the comparison can be made with no issues.
So, you can do the following modification to your script:
for (let i = values.length - 1; i >= 0; i--) {
var row = values[i];
var found = false;
for (let k = 0; k < row.length; k++)
for (let j = 0; j < arrayOfWords.length; j++)
if (row[k].toString().toLowerCase().indexOf(arrayOfWords[j]) > -1)
found = true;
if (found == true) {
sheet.deleteRow(i + 1);
rowsDeleted++;
}
}
The above script loops through all the values from the data range, hence the other loop added, and then making use of the toLowerCase() function, checks if the value is part of the arrayOfWords array and if needed, deletes the row(s). The found variable is used here to indicate whether the word has been found or not.
Reference
JavaScript String.prototype.toLowerCase().

Get the first empty row in multidimensionnal array

I have a question about a function where I'm trying to get a first look at a multidimensional array.
To explain my problem: In a sheet, I manage my roadmap with projects. A project is composed by 4 rows where I have some information (Project Name, Estimated Team, Timeline ...).
And In my timeline, I need to retrieve the first empty rows in multiple arrays (the first non empty is the startDate).
The problem, I have 4 teams in this multidimensional array, and (for example), the start date can be in the 1st team array for the project A, but the start date can be also in the 3rd team array for the project B.
In my function, I'm trying to get to the start date, but my first step is to check the first array ... (projectRange and after in the code)
So ... I think the best way should be check every rows in the first column, and continue like this to the getLastColumn, right?
So, how can I manage my Loop with this way?
function findLastRow(column) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(roadmapSS);
var startRow = 11;
var startCol = 11;
var dataLength = sheet.getLastRow()-(startRow+2);
var rangeData = sheet.getRange(startRow, 2, sheet.getLastRow(), sheet.getLastColumn());
var dataValues = rangeData.getValues();
var projectsList = rangeData.getValues();
var projectDatas = {};
var projectRange = null;
var projectName = null;
var projectPlan = {};
var realStart = null;
for (var i = 0; i < dataLength; i+=4) {
projectDatas = projectsList[i];
var step = startRow+i;
var realStartRange = startRow+i+1
for (var j = 0; j < 1; j+=4) {
projectName = projectDatas[j];
}
projectRange = ([step, startCol, 4, sheet.getLastColumn()]).toString();
var projectPlan = sheet.getRange(step, startCol, 4, sheet.getLastColumn()).getValues();
for (var k = 0; k < projectPlan.length; ++k) {
realStart = projectPlan[k];
for (var l = 0; l < realStart.length; ++l) {
if (realStart[l] != '') {
break;
}
}
}
//sheet.getRange(realStartRange, 2).setValue(columnToLetter([l]))
console.log(projectName, [l], columnToLetter([l]));
}
}
In fact, i'm trying to get the first column of B in this example (because it's the first non empty occurence :
var projectTimeline = [
['','','A','A','','A'],
['','B','B','','B',''],
['','','','C','C',''],
['','','','','D','D']
]
I found my solution :
I throughed the column first, and then the row. It worked, I founded the column :)
function findFirstDate (timeline) {
for (var col = 0; col < timeline[0].length; col++) {
for (var row = 0; row < 4; row++) {
if (timeline[row][col] != '') {
return [col];
}
}
}
}

Merge two arrays by value

I have a specific cuestion about merge arrays:
I'm using google charts and I need to do something like this
Combo Chart
To do something like that I need to fill this matrix
I did fine with axis x and axis y:
$scope.data= [];
$scope.data[0]= ['Months'];
angular.forEach($scope.consultors, function(consultor) {
$scope.data[0].push(consultor.no_user);
})
angular.forEach(months, function(month) {
$scope.data.push([month])
})
but, my problem is when i try to put $scope.relatorias, inside of $scope.data.
This is $scope.relatorias, this variable has the data of every consultor group by month, like this:
If you open each array look like this
I just need push ganancias_netas, but my problem is when there is an empty month, for example anapaula has data in every month but renato hasn't.
I have try to user for or for each but is doesn't work, I'm not an expert in matrix and this is my first time working on it.
fiddle: http://fiddle.jshell.net/rfcabal/5ftw7c8d/
/// UPDATE ///
I added this code that first fill with 0 $scope.data and then search for the values in relatorios and shoudl fill $scope.data, but for some reason jus fill with the last found value.
for (var i = 1; i < $scope.data.length; i++) {
for (var a = 1; a < $scope.data[0].length; a++) {
$scope.data[i][a] = 0;
for (var b = 0; b < $scope.relatorios[a-1].length; b++) {
console.log(a-1+' '+b+' '+3);
console.log($scope.relatorios[a-1][b]['ganancias_netas'])
$scope.data[i][a] = $scope.relatorios[a-1][b]['ganancias_netas'];
}
}
}
Thanks for your help
I just solved with 2 for
First i fill every data space with 0
for (var i = 1; i < $scope.data.length; i++) {
for (var a = 1; a < $scope.data[0].length; a++) {
$scope.data[i][a] = 0;
}
}
The i jus remplace where fecha_emision equal to position 1 of every array.
for (var a = 0; a < $scope.relatorios.length; a++) {
for (var b = 0; b < $scope.relatorios[a].length; b++) {
for (var i = 1; i < $scope.data.length; i++) {
var index = $scope.data[i].indexOf($scope.relatorios[a][b]['fecha_emision']);
if(index >= 0) {
$scope.data[i][a+1] = parseFloat($scope.relatorios[a][b]['ganancias_netas']);
}
}
}
}

Unable to append row through javascript function

Im trying to append a row to a table. And this is the code that i tried
function load() {
for (var k = 0; k < 1; k++) {
var myTableDiv = document.getElementById("pID");
var tableBody = document.getElementById("tBody");
var table = document.getElementById('pTable');
table.appendChild(tableBody);
for (var i = 0; i < 3; i++) {
var tr = document.getElementById('RiskRow');
tableBody.appendChild(tr);
for (var j = 0; j < 1; j++) {
var element1= document.getElementById('RiskTD');
var element2= document.getElementById('severityTD');
var element3= document.getElementById('mitigationTD');
var element4= document.getElementById('contingencyTD');
var element5= document.getElementById('riskStatusTD');
tr.appendChild(element1);
tr.appendChild(element2);
tr.appendChild(element3);
tr.appendChild(element4);
tr.appendChild(element5);
}
}
myTableDiv.appendChild(table);
}
}
But this results in appending only one row. I need multiple rows to be added that follows the for loop. Im not sure where i stuck up.
Regardless of the redundant loops you have there (var k = 0; k < 1 will always perform once...), the reason you're seeing this behavior is because you're trying to append the same element over and over. This will cause the element to be moved, rather than copied.
Have a look at document.cloneNode

Using Google Script to get an array from a list of cells in google sheet

How do I create an array of values that are in a set of cells in Google sheets?
The array should be the same rows and columns as the cells and it should have the same values as the sheet has in each location.
Also, I want to be able to pass the range of the array in as a parameter so that I can use the function for different ranges.
edit 2:
New code, nearly working, I just need to have it receive the ranges from user input on the google sheet itself. This is what I am trying to get work, but the beginning is struggling to work, I can't pass in a choice of ranges and have the cell update and have the function run.
Also, I am having a problem with getting a reference error almost every time even when I try to preset the ranges within the function without any parameters
function sortingtest(pWO, pInfo, pSearch) {
var WO = SpreadsheetApp.getActiveSpreadsheet().getRange(pWO).getValues();
var Info = SpreadsheetApp.getActiveSpreadsheet().getRange(pInfo).getValues();
var Search = SpreadsheetApp.getActiveSpreadsheet().getRange(pSearch).getValues();
//[row][column]
var FinalArray1 = [];
var FinalArray2 = [];
var FinalArray3 = [];
var LastArray = [];
var a = 0;
var b = 0;
var c = 0;
var d = 0;
for (var row = 0; row < WO.length; row ++) {
var counter = row - 1;
while (WO[row] == "") {
WO[row] = WO[counter];
counter--;
}
}
for (var col = 0; col < Info[0].length; col++) {
for (var row = 0; row < Info.length; row++) {
if (Info[row][col] == Search[col]) {
if (col == 0) {
FinalArray1[a] = WO[row];
a++;
}
else if (col == 1) {
FinalArray2[b] = WO[row];
b++;
}
else if (col == 2) {
FinalArray3[c] = WO[row];
c++;
}
}
}
}
for (var i = 0; i < FinalArray1.length; i++) {
for (var j = 0; j < FinalArray2.length; j++) {
for (var k = 0; k < FinalArray3.length; k++) {
if (FinalArray3[k] == FinalArray2[j] && FinalArray2[j] == FinalArray1[i]) {
LastArray[d] = FinalArray1[i];
d++;
}
}
}
}
return LastArray;
}
If you call your function from within the spreadsheet as you indicate it in your comment (=sortingtest(sheet1!A1:C12,sheet3!D1:E12,sheet2!F1:G4)), you do not need to call any of the SpreadsheetApp functions to get arrays: pWO, pInfo and pSearch will already be 2 dimensional arrays.
Quoting the Google custom function article:
If you call your function with a reference to a range of cells as an argument (like =DOUBLE(A1:B10)), the argument will be a two-dimensional array of the cells' values. For example, in the screenshot below, the arguments in =DOUBLE(A1:B2) are interpreted by Apps Script as double([[1,3],[2,4]]).

Categories