Блог

Массивы Google Apps Script и JavaScript. Метод .filter()

Краткое описание метода

Метод .filter() последовательно сравнивает все элементы массива с условием фильтра, указанным в callback функции (функции обратного вызова). То есть метод, по сути, представляет собой цикл for, внутри которого callback функция последовательно обрабатывает все элементы массива.

Метод .filter() может передавать в callback функцию три параметра:

  1. текщее значение элемента массива item
  2. необязательный параметр index - индекс текущего элемента
  3. необязательный и редко используемый параметр array

Для того, чтобы элемент, удовлетворяющий условию фильтра, был выбран, callback функция должна вернуть значение true.

Все элементы, для которых callback функция вернула знечение true, собираются в отдельный массив, который может быть сохранён в новом массива (в примерах ниже это массив result2).

В трёх вариантов примера, представленных ниже (examples #1, #2, #3) рассмотрено одно и то же условие фильтра - отобрать все элементы массива со значением > 3.

В последнем примере (наиболее оптимальным с точки зрения краткости кода) логическое выражение, вместо оператора if , поставлено непосредственно в return . Поскольку, как известно, логическое выражение может принимать только 2 значения: true или false.

const arr = [1, 2, 9, 4, 1, 6, 5];
    
    let result = arr.filter((item, idx, arr) => console.log(item, idx, arr));
//  [20-06-15 14:53:21:082 EEST] 1 0 [ 1, 2, 9, 4, 1, 6, 5 ]
//  [20-06-15 14:53:21:084 EEST] 2 1 [ 1, 2, 9, 4, 1, 6, 5 ]
//  [20-06-15 14:53:21:085 EEST] 9 2 [ 1, 2, 9, 4, 1, 6, 5 ]
//  [20-06-15 14:53:21:086 EEST] 4 3 [ 1, 2, 9, 4, 1, 6, 5 ]
//  [20-06-15 14:53:21:087 EEST] 1 4 [ 1, 2, 9, 4, 1, 6, 5 ]
//  [20-06-15 14:53:21:089 EEST] 6 5 [ 1, 2, 9, 4, 1, 6, 5 ]
//  [20-06-15 14:53:21:090 EEST] 5 6 [ 1, 2, 9, 4, 1, 6, 5 ]
    
    console.log(result);
//  [20-06-15 14:53:21:091 EEST] []  

// variant #1
  let result2 = arr.filter(item => {
      if (item > 3) {
        return true;
      } else {
        return false;
      };
    });
    
// variant #2
    result2 = arr.filter(item => {
      return item > 3;
    });


// variant #3
    result2 = arr.filter(item => item > 3);
    
    console.log(result2); // [ 9, 4, 6, 5 ]
    
    var sf = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
    let data = sf.getDataRange().getValues().slice(1);
    
    console.log(data);
//  [ [ '', false, 'apples', 5 ],
//    [ '', true, 'carrots', 12 ],
//    [ '', false, 'grapes', 4 ],
//    [ '', false, 'plums', 3 ],
//    [ '', true, 'strawberry', 9 ],
//    [ '', false, 'perches', 4 ],
//    [ '', false, 'bananas', 1 ] ]   

    let newData = data.filter(item => item[1]);

    console.log(newData); 
// [ [ '', true, 'carrots', 12 ], 
//   [ '', true, 'strawberry', 9 ] ]

И ещё одни полезный скрипт, который не вошёл в это видео - скрипт, который фильтрует ТОЛЬКО уникальные значения.

Идея очень проста: для каждого элемента массива находится его индекс и сравнивается с текущим индексом значения этого элемента в массиве. Если элемент не встретился ранее, то значения индексов будут равны, и функция uniqValue вернёт true .

В первом примере, для лучшего понимания алгоритма, функция uniqValue написана отдельно. Во втором - непосредственно "встроена" в метод .filter

let arrayNotUniq = [1, 5, 9, 5];

//variant #1

function uniqValues(item, index, arr) {
  return arr.indexOf(item) === index;
}

let arrayUniq = arrayNotUniq.filter(uniqValues)                        // [1, 5, 9]



//variant #2

  let arrayUniq = arrayNotUniq.filter((item, index, arr) => {
    return arr.indexOf(item) === index;
  });                                                                                                     // [1, 5, 9]

Дополнительную информацию вы можете найти в этом видео:

Читать дальше >>

Массивы Google Apps Script и JavaScript. Метод .map()

Краткое описание метода

Метод .map() последовательно перебирает все элементы массива. То есть, по сути, представляет из себя цикл for, где переменная цикла изменяется от индекса первого элемента до последнего.

Этот метод может передавать в callback функцию 3 элемента:

  1. текщее значение элемента массива currentValue (в примере #2 переменная x)
  2. необязательный параметр index - индекс текущего элемента (в примере #2 переменная y)
  3. необязательный и редко используемый параметр array (в примере #2 переменная z)

В примере #1 приводится умножение кажного элемента массива на 2. То есть callback функция каждый раз возвращает элемент массива, умноженный на 2.

В примере #2 проводится исследования параметров метода. Поэтому callback функция ничего не возращет, а только выводит на печать передаваемые её параметры. В каждом цикле это: значение элемента массива, его индекс и сам массив.

var arr = [1, 2, 3];

  // example #1
  var arr2 = arr.map(x => x * 2); // [ 2, 4, 6 ]

  // example #2
  var arr2 = arr.map((x, y, z)  => {
    console.log(x + "|" + y + "|" + z);
  }); 
 
//  1|0|1,2,3
//  2|1|1,2,3
//  3|2|1,2,3

Рефакторинг кода с помощью метода .map()

Скрипты, представленные ниже, обрабатывают один и тот же массив двумя способами:

1.) с помощью цикла for

var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var arrM = ss.getRange(1, 1, 10).getValues(); // [[10. Will Smith: $35 million], [9. Paul Rudd: $41 million], [8. Chris
  var arrW = ss.getRange(14, 1, 10).getValues(); // [[10. Ellen Pompeo: $22 million], [9. Charlize Theron: $23 million], [8. 
  
  var arr = arrM.concat(arrW);
  
  for (var i = 0; i < arr.length; i++) {
    arr[i][0] = arr[i][0].replace(":", "").replace("$", "").replace(" (tie)", "(tie)").replace(" Jr", "Jr"); // 
    arr[i] = arr[i][0].split(' '); // [10., Will, Smith, 35, million]
    arr[i][1] = arr[i][1] + " " + arr[i][2]; // [10., Will Smith, Smith, 35, million]
    arr[i].splice(2, 1); // [10., Will Smith, 35, million]
    if (i < 10) {
      arr[i][3] = 'man';
    } else {
      arr[i][3] = 'woman';
    };
  };
  
  arr.sort(function(a, b) {
    return b[2] - a[2];  
  });

  for (var i = 0; i < arr.length; i++) {
    arr[i][0] = i + 1;
    Logger.log(arr[i]);
  };
  
  ss.getRange(2, 6, arr.length, arr[0].length).setValues(arr);

2.) и с помощью метода .map():

var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var arrM = ss.getRange(1, 1, 10).getValues(); // [[10. Will Smith: $35 million], [9. Paul Rudd: $41 million], [8. Chris
  var arrW = ss.getRange(14, 1, 10).getValues(); // [[10. Ellen Pompeo: $22 million], [9. Charlize Theron: $23 million], [8. 
  
  var arr = arrM.concat(arrW);
  
  var arr2 = arr.map(x => x[0].replace(":", "").replace("$", "")
                              .replace(" (tie)", "(tie)").replace(" Jr", "Jr")
                              .split(' '));  // [ [ '10.', 'Will', 'Smith', '35', 'million' ],
  
  arr = arr2.map(x => [x[2], x[1] + ' ' + x[2], x[3], x[4]]);
  arr = arr.map((x, i) => {
    (i < 10) ? x[3] = 'man' : x[3] = 'woman' ;
    return x;
  });   //  [ [ 'Smith', 'Will Smith', '35', 'man' ],
  
  arr.sort((a, b) => a[0].localeCompare(b[0])); // [ [ '1.', 'Dwayne Johnson', '89.4', 'man' ],
  
  arr = arr.map((x, i) => {
    x[0] = i + 1;
    return x;
  });
  
 
  console.log(arr);
  ss.getRange(2, 6, arr.length, arr[0].length).setValues(arr);

Дополнительную информацию вы можете найти в этом видео:

Читать дальше >>

Массивы Google Apps Script и JavaScript. Метод сортировки массивов .sort()

Предисловие.

Прежде чем перейти к рассмотрению метода .sort, необходимо хотябы несколько слов сказать о функциях, используемых в JavaScript и Google Apps Script.

Higher-order functions (Функции более высокого порядка) - называются функции, использующие в качестве аргументов и/или возвращаемых объектов функции первого класса. (Пример - функция .sort)

First-class functions (Функции первого класса) используются в качестве аргументов и/или возвращаемых объектов для функций более высокого порядка. (Пример - функции, являющиеся аргументами для функции .sort)

const arr = [1, 2, 3, 10, 20];
  
// ========== Higher-order functions ===============

  //arr.sort(sortFunction)
  
// ========== First-class functions ================

  // function definition 
  function twoTimes(x) {
    return x * 2;
  }; 
  
  // another function definition
  var twice;
  twice = function(x) {return x * 2};
  
  // arrow function
  twice = (x) => x * 2;
  
  // arrow function with 1 argument
  twice = x => x * 2;
  
  console.log(twice(3));
  console.log(typeof twice);
  
  // 1.) function as argument another function
  var fourTimes = (x, twice) => twice(x);
  
  console.log(fourTimes(3, twice)); // 6
  
  //  2.) return function
  var fourTimes = (x) => x * 4;
  
  var xTimes = (x, order) => {
    if (x / order == 4) {
      return fourTimes;
    } else if (x / order == 2) {
      return twice;
    };
  };
  
  var y = xTimes(12, 3); // 8

  console.log(y(2)); // =6
  console.log('y is ' + typeof y); // y is function

Примеры сортировки массивов

// numbers sorting
  
  const arr = [1, 2, 10, 20];
  
  arr.sort((a, b) => a - b); // [ 20, 10, 2, 1 ]
  arr.sort((a, b) => b - a); // [ 1, 2, 10, 20 ]
  
  // string sorting
  
  const arr1 = ['a', 'b', 'A', 'B', 'г', 'Г', 'д', 'Д']; 
  
  arr1.sort();                             // [ 'A', 'B', 'a', 'b', 'Г', 'Д', 'г', 'д' ]
  arr1.sort((a, b) => a.localeCompare(b)); // [ 'a', 'A', 'b', 'B', 'г', 'Г', 'д', 'Д' ]
  arr1.sort((a, b) => b.localeCompare(a)); // [ 'Д', 'д', 'Г', 'г', 'B', 'b', 'A', 'a' ]
  
  
// 2-dimensional array
  
    var arr_2 = [[1, 5, 6],
                 [3, 7, 9],
                 [9, 2, 1]];
                 
    arr_2.sort((a, b) => b[1] - a[1]); // [ [ 3, 7, 9 ], 
                                       //   [ 1, 5, 6 ], 
                                       //   [ 9, 2, 1 ] ]

// JSON objects

  var actors = [
    {name: 'Dwayne Johnson', income:	89.4},	
    {name: 'Chris Hemsworth', income: 76.4},
    {name: 'Robert DowneyJr.', income: 66},
    {name: 'Akshay Kumar', income: 65},
    {name: 'Jackie Chan', income: 58},
  ];
  
  actors.sort((a, b) => a.income - b.income); 
                   //[ { name: 'Jackie Chan', income: 58 },
                   //  { name: 'Akshay Kumar', income: 65 },
                   //  { name: 'Robert DowneyJr.', income: 66 },
                   //  { name: 'Chris Hemsworth', income: 76.4 },
                   //  { name: 'Dwayne Johnson', income: 89.4 } ]
                
  actors.sort((a, b) => a.name.localeCompare(b.name)); 
                  // [ { name: 'Akshay Kumar', income: 65 },
                  //   { name: 'Chris Hemsworth', income: 76.4 },
                  //   { name: 'Dwayne Johnson', income: 89.4 },
                  //   { name: 'Jackie Chan', income: 58 },
                  //   { name: 'Robert DowneyJr.', income: 66 } ]

Дополнительную информацию вы можете найти в этом видео:

Читать дальше >>

Создание массивов Apps Script. Добавление, Удаление и Изменение Элементов Массивов

Массивы в Apps Script можно создать несколькими способами:

  1. Декларировать в строке кода имя массива и приравнять его к последовательности значений, взятых в квадратные скобки;
  2. Считать данные с листа Google SpreadSheet помощью метода .getValues() ;
  3. Разделив строку с помощью метода .split([delimiter]) (обычно в качестве разделителя delimiter используется пробел;
  4. Пп 3, когда в качестве строки используется слово, а в качестве разделите - пустая строка.

function createArray() {
  
// ======== HOW TO CREATE ARRAY? ===========

  // 1.) Write
  var arr = [1, 2, 3];
  
  // 2.) Read from spreadsheet
  var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var arr1 = ss.getDataRange().getValues(); // [[1.0, 2.0, 3.0]]
  
  // 3.) Using string splitting
  var string = 'I am learning Google Spreadsheet';
  var arr2 = string.split(' '); // [I, am, learning, Google, Spreadsheet]
  
  // 4.) Using string splitting
  var arr3 = 'word'.split(''); // [w, o, r, d]
  
}

Добавлять элементы в массив можно разными способами:

  1. Самый простой из них - приравнять определённый элемент массива (с помощью указания его индекса в квадратных скобках) к нужному значению.

  2. Если максимальное значение индекса будет превышено более, чем на единицу, то всем элементы между старым и новым максимальными индексами автоматически присваивается значение null.

  3. Добавление элементов в конец массива с помощью метода .push(value) ;
  4. Добавление элементов в начало массива с помощью метода .unshift(value) ;
  5. Удаление элемента с максимальны индексом .pop() ;

  6. Значение удаляемого элемнта можно при этом присвоить новой переменной.

  7. Удаление элемента с нулевым индексом .shift() ;
  8. Адресное изменение элементов массива с помощью метода .splice(index, hawManyDelete, whatInsert) ;

  9. где:
    • index - номер индекса, с которого будет производиться вставка / удаление индексов;
    • hawManyDelete - число элементов, которые необходимо удалить. Если hawManyDelete = 0, удаления не будет;
    • whatInsert - значене элемента (элементов), который (которые) будет вставлены, начиная в индекса номер index .
  10. Метод изменения порядка индексов в массиве с прямого на обратный и наоборот.reverse() ;
  11. Метод сортировки элементов массива .sort() . Без параметра в виде функции используется крайне редко из-за своей ограниченности: сортирует только строки в юникоде по возрастанию.

function addDeleteItems() {

// ================ ADD ITEMS =======================

  // 1.) [index]
  var arr4 = [1, 2, 3];
  arr4[3] = 4; // [1.0, 2.0, 3.0, 4.0]
  arr4[5] = 6; // [1.0, 2.0, 3.0, 4.0, null, 6.0]
  arr4[-1] = 0; // [1.0, 2.0, 3.0, 4.0, null, 6.0]
  
  // 2.) .push(value)
  arr4.push(7); //  [1.0, 2.0, 3.0, 4.0, null, 6.0, 7.0]
  
  // 3.) .unshift(value)
  arr4.unshift(0) // [0.0, 1.0, 2.0, 3.0, 4.0, null, 6.0, 7.0]
  
// ================= DEL ITEMS ======================

  // 4.) .pop()
  var x = arr4.pop(); // [0.0, 1.0, 2.0, 3.0, 4.0, null, 6.0]
  
  // 5.) .shift()
  var x = arr4.shift(); // [1.0, 2.0, 3.0, 4.0, null, 6.0]
  
// ===== ADD (INSERT), DELETE, CAHGE =================

  // 6.) .splice(index, hawManyDelete, whatInsert)
  arr4.splice(4, 0, 5); // [1.0, 2.0, 3.0, 4.0, 5.0, null, 6.0]
  arr4.splice(5, 1);    // [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]

// ============ CHANGE ORDER =========================
  // 7.) .reverse()
  arr4.reverse(); // [6.0, 5.0, 4.0, 3.0, 2.0, 1.0]
  
  // 8.) .sort()
  arr4 = [6.0, 5.0, 404.0, 41.0, 4.0, 3.0, 2.0, 1.0];
  arr4.sort();
}

Больше информации и примеров вы сможете найти в этом видео:

Читать дальше >>

Бесконечные Зависимые Выпадающие Списки в Google Sheets (часть 2)

Данная статья является продолжением статьи Бесконечные Зависимые Выпадающие Списки в Google Sheets.

Здесь описаны изменения изменения и дополнения кода, а именно:

  • Автоматическое создание листов Номе Data_2;
  • Автоматическое добавление всех формул на листе Номе;
  • При редактировании созданной ранее строки зависимых (связанных списков), автоматически удаляются значения и формулы справа от редактируемой ячейки.

function onEdit(e) {

  var row = e.range.getRow();
  var col = e.range.getColumn();
  var list_name = e.source.getActiveSheet().getName();
  var name = e.value;
  var oldName = e.oldValue;
  var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Home');
  var mask = JSON.stringify(sh.getRange(row, 1, 1, col).getValues()[0]);
  var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Home');
  var colMax = sh.getLastColumn();
  
  if(list_name === "Home" && name !== oldName && col < colMax) {
    fillColumn(row, col, mask);
  }
}

function onOpen() {
  var ui = SpreadsheetApp.getUi();
  // Or DocumentApp or FormApp.
  ui.createMenu('Custom Menu')
      .addItem('Create sheets', 'createSheets')
      .addToUi();
}


function fillColumn(row, col, mask) {

// clear dataVal and Value
  var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Home');
  var colMax = sh.getLastColumn();
  sh.getRange(row, col + 1, 1, colMax).clearDataValidations().clearContent();

// find date
  var sd = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data");
  var arrData = sd.getDataRange().getValues();
  var arrData_2 = [];
  arrData_2.push(arrData[0]);
  
  var iMax = arrData.length - 1;
  for(var i=1; i<=iMax; i++) {
    if(JSON.stringify(arrData[i].slice(0, col)) == mask) {
      arrData_2.push(arrData[i]);
    }
  }
   
// clear Data_2
  var sd_2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data_2");
  sd_2.getDataRange().clearContent();

// insert data
  sd_2.getRange(1, 1, arrData_2.length, arrData_2[0].length).setValues(arrData_2);
  
// add dataVal
  col++;
  var list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'];
  var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Home');
  sh.getRange(row, col).setDataValidation(SpreadsheetApp.newDataValidation()
  .setAllowInvalid(false)
  .requireValueInRange(sh.getRange('Data_2!$' + list[col - 1] + '$2:$' + list[col - 1] + '$1000'), true)
  .build());
}

function createSheets() {

// is exist Home?
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sd = ss.getSheetByName('Data')
  
// create if not exist
  if(!ss.getSheetByName('Home')) {
    ss.insertSheet('Home', 0);
// create Data Val
    var sh = ss.getSheetByName('Home');
    sh.getRange('Home!A2:A20').setDataValidation(SpreadsheetApp.newDataValidation()
      .setAllowInvalid(false)
      .requireValueInRange(sh.getRange('Data!$A$2:$A'), true)
      .build());
    sh.getRange(1, 1, 1, 10).setValues(sd.getRange(1, 1, 1, 10).getValues()).setFontWeight('bold');

  };

// is exist Data_2?
  if(!ss.getSheetByName('Data_2')) {
   
// create if not exist
    var k = ss.getNumSheets();
    ss.insertSheet('Data_2', k + 1);
    var sd_2 = ss.getSheetByName('Data_2');
    sd_2.getRange(1, 1, 1, 10).setValues(sd.getRange(1, 1, 1, 10).getValues()).setFontWeight('bold');
  };
}

Теперь, в этой версии программы появилась возможность по нажатию всего лишь одной(!) кнопки из пользовательского меню автоматически создавать все листы файла, необходимые для работы скрипта (включая форматирование и валидацию данных).

Всё что для этого нужно:

  1. cоздать лист с именем "Data"
  2. скопировать в него данные, необходимые для связанных (зависимых) списков
  3. скопировать и встваить в файл этот скрипт
  4. обновить страницу
  5. в появившемся пользовательском меню выбрать: Create sheets

Дополнительную информацию вы можете получить из этого видео:

ВНИМАНИЕ!


У этой статьи есть продолжение: Бесконечные Зависимые Выпадающие Списки в Google Sheets (часть 3)





Читать дальше >>

Бесконечные Зависимые Выпадающие Списки в Google Sheets

В статье рассмотрен вариант создания на листе Google Spreadsheet связанных выпадающего списков, практически не ограниченных ни по числу связанных элементов, ни по количству сток на листе.

Скрипт программы представлен ниже:

function onEdit(e) {

  let col = e.range.getColumn();
  let list_name = e.source.getActiveSheet().getName();
  let name = e.value;
  
  if(list_name=="Home") {
    fillColumn(col, name);
  }
}

function fillColumn(col, name) {

// find date
  var sd = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data");
  var iMax = sd.getLastRow();
  var col_data = [];
  
  for(var i=2; i<=iMax; i++) {
    var x = sd.getRange(i, col).getValue();
    if(x == name) {
      col_data.push(sd.getRange(i, col+1).getValue());
    }
  }
   
// clear Data_2
  var sd_2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data_2");
  sd_2.getRange(2, col+1, sd_2.getLastRow(), sd_2.getLastColumn()).clearContent();


// insert data
  iMax = col_data.length;

  for(var i=2; i<=iMax+1; i++) {
    sd_2.getRange(i, col+1).setValue(col_data.shift());
  }
}

Обратите внимание: имена листов должно ТОЧНО соответствовать именам, указанным в скрипте!

Главный лист: "Home"

лист данных: "Data",

лист промежуточных данных: "Data_2"

Эти имена в скрипте выделены красным цветом, поэтому, при желании, их будет не трудно найти, чтобы заменить на имена свои листов.


У этой статьи есть продолжение: Бесконечные Зависимые Выпадающие Списки в Google Sheets (часть 2)

Теперь, в новой версии программы появилась возможность по нажатию всего лишь одной(!) кнопки из пользовательского меню автоматически создавать все листы файла, необходимые для работы скрипта (включая форматирование и валидацию данных).


Читать дальше >>

Генератор документов Google Apps Script с журналом учёта созданных документов

Эта статья является продолжением другой, более ранней статьи Как автоматически вставлять данные в Google Docs, используя Google Apps Script?, в который описывается алгоритм создания копии шаблона документа Google Doc и вставки в него новых данных, полученных из листа Google Spreadsheet.

Здесь мы рассмотрим новую версию программы генерации документов, которая претерпела ряд существенных изменений.

В первую очередь изменения связаны с тем, что в любом мало-мальски приличном делопроизводстве рано или поздно неизбежно встаёт вопрос об учёте созданных документов. Поэтому в новой редакции программы был добавлен журнал регистрации созданных (сгенерированных) документов, где появилось 2 новых поля: номер документа {document_id} и дата документа {document_date}. (В этой версии программы данные новых полей изменяются вручную). Разумеется, при желании этот список может быть существенно дополнен. Главное, не забывать аккуратно копировать уникальные символы новых полей в соответствующие места вставки шаблонов Google Docs.


Журнал регистрации было решено сделать на главном листе ("Main"). Что существенно изменило интерфейс программы. Данные контрагентов переместились на новый лист "Clients":

А поля с чек-боксами для выбора "ОТ КОГО" (FROM) и "КОМУ" (TO) были заменены полями со списком контрагентов, связанным с листом "Clients".

Добавлен также ещё один вариант запуска скрипта - из пользовательского меню (My menu), которое создаётся автоматически при открытии документа.

Перед запуском скрипта теперь необходимо выбрать в новой строке контрагентов FROM и TO, наименование шаблона Google Docs, номер документа и дату документа. И самое главное помнить, что в новом скрипте генерируется тот документ, в строке которого находится активная ячейки.

Полностью обновлённый скрипт программы представлен ниже:

//create user's menu
function onOpen() {
  
  var ui = SpreadsheetApp.getUi();
  ui.createMenu("My menu")
    .addItem('Create Document', 'DocGenerator')
    .addToUi()
}

 //main function
function DocGenerator() {

  var sm = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Main");
     
  //find clients info
  var row_active = sm.getActiveCell().getRow();
  var principal_shart_name = sm.getRange(row_active, 1).getValue();
  var attorney_shart_name = sm.getRange(row_active, 2).getValue();
  var principal = getClientData(principal_shart_name);
  var attorney = getClientData(attorney_shart_name);
  
  //find documents info
  var document = {
    id:sm.getRange(row_active, 4).getValue(),
    date:sm.getRange(row_active, 5).getValue(),
    }
  
  // find template
  var docName = sm.getRange(row_active, 3).getValue();
  var docID = getTemlateID(docName);
  var docNameCopy = docName +"_" + document.id +"_" + document.date + "_" + principal.name + "_" + attorney.name;
  var file = DriveApp.getFileById(docID);
  file.makeCopy(docNameCopy);
  var fileCopy = DriveApp.getFilesByName(docNameCopy).next();
  var fileCopyID = fileCopy.getId();
  
  // replacement
  var body = DocumentApp.openById(fileCopyID).getBody();
  body.replaceText('{document_id}', document.id);
  body.replaceText('{document_date}', document.date);
  
  body.replaceText('{principal_name}', principal.name);
  body.replaceText('{principal_id}', principal.id);
  body.replaceText('{principal_id_dateOfIssue}', principal.id_dateOfIssue);
  
  body.replaceText('{attorney_name}', attorney.name);
  body.replaceText('{attorney_id}', attorney.id);
  body.replaceText('{attorney_id_dateOfIssue}', attorney.id_dateOfIssue);
  
  Browser.msgBox("Completed!");    
  
}

function getClientData(sh_name) {

  var sc = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Clients");
  var iMax = sc.getLastRow();
  
  for(var i=2; i <= iMax; i++) {
    
    if(sc.getRange(i, 1).getValue() == sh_name) {
      break;    
    }
  }

  var client = {
    short_name: sc.getRange(i, 1).getValue(),
    name: sc.getRange(i, 2).getValue(),
    id: sc.getRange(i, 3).getValue(),
    id_dateOfIssue: sc.getRange(i, 4).getValue(),
    };
  return client;
}

function getTemlateID(docName) {

  var st = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Templates');
  var iMax = st.getLastRow();
  
  for(var i=2; i <= iMax; i++) {
    
    if(st.getRange(i, 1).getValue() == docName) {
      var docId = st.getRange(i, 2).getValue();
      return docId;    
    }
 
  }

}

Более подробные объяснения о работе скрипта Вы сможете получить, ознакомившись с данным видео:

Читать дальше >>

Пакетная распаковка zip-файлов

Этот скрипт будет очень полезен, если необходимо распаковать большое количество zip-файлов.

где folder_with_zip_files - имя папки с zip-файлами.

Результат (распакованные файлы) будет размещён в той же папке.

#!/usr/bin/env python3

from zipfile import ZipFile
import glob, os

zfiles = []
folder_with_zip_files = "/home/su/Downloads/unzipping"
os.chdir(folder_with_zip_files )
for file in glob.glob("*.zip"):
    zfiles.append(file)

for zfile in zfiles:
   with ZipFile(zfile, 'r') as zipObj:
      zipObj.extractall()
Читать дальше >>

Как автоматически вставлять данные в Google Docs, используя Google Apps Script?

Очень часто возникает необходимость создавать однотипные документы, в которых изменяются только данные контрагентов: договора, доверенности, коммерческие предложения и т.д.

Совершенно логичное решение в подобной ситуации:

  • создать шаблоны документов Google Docs, куда будут вставляться данные контрагентов;
  • создать документ Google Sheets, где будут хранится данные контрагентов и ссылки на шаблоны документов,
  • и написать скрипт, который будет управлять процессом выбора контрагентов и вставки данных выбранных контрагентов в шаблоны документов.

Сама идея определения места вставки и последующей вставки на это место нужного значения заключается в создании на листе Google Doc уникального набора символов, который можно было бы однозначно идентифицировать и заменить с помощью метода замены текста replaceText:

body.replaceText("{unique label}", "klient data");

Начнём с таблицы. В нашем примере мы сделали три "информационных столбца": имя клиента, номер ID, дата издания ID. Чтобы облегчить редактирование шаблона имена столбцов мы назовем их также, как и имена меток в Google Docs. И, поскольку это - метки (места вставок), то сразу определим для себя, что метки мы всегда будем заключать в фигурные скобки.

Помимо информационных столбцов здесь также находятся "вспомогательные столбцы", с помощью которых реализован интерфейс нашей программы: КТО выдаёт (кто заключает договор), КОМУ выдаёт (с кем заключает договор) и какой ШАБЛОН документа при этом используется.

В качестве шаблона документа, к которому обращается скрипт, был использован следующий вариант доверенности.

Для того, чтобы различить метки доверителя и поверенного, к меткам доверителя был добавлен элемент текста - [principal_], а к меткам поверенного - [attorney_].

Скрипт, представленный ниже, реализует следующие задачи:

  • определение выбранных клиентов и чтение данных этих клиентов с листа Google Sheets;
  • нахождение выбранного шаблона в Google Drive, создание копии выбранного шаблона и сохранение её под новым именем;
  • открытие документа (только что созданной копии шаблона) и замене меток на данные контрагентов.

function DocGenerator() {

  var sc = SpreadsheetApp.getActiveSheet();
  
  //find clients info
  
  var principalIndex = getClientIndx(1);
  var attorneyIndex = getClientIndx(2);
  var principal = getClientData(principalIndex);
  var attorney = getClientData(attorneyIndex);
  
  // find template
  var docName = sc.getRange(principalIndex, 3).getValue();
  var docID = getTemlateID(docName);
  var docNameCopy = docName + "_" + principal.name + "_" + attorney.name;
  var file = DriveApp.getFileById(docID);
  file.makeCopy(docNameCopy);
  var fileCopy = DriveApp.getFilesByName(docNameCopy).next();
  var fileCopyID = fileCopy.getId();
  
  
  // replacement
  var body = DocumentApp.openById(fileCopyID).getBody();
  body.replaceText('{principal_name}', principal.name);
  body.replaceText('{principal_id}', principal.id);
  body.replaceText('{principal_id_dateOfIssue}', principal.id_dateOfIssue);
  body.replaceText('{attorney_name}', attorney.name);
  body.replaceText('{attorney_id}', attorney.id);
  body.replaceText('{attorney_id_dateOfIssue}', attorney.id_dateOfIssue);
  
  Browser.msgBox("Completed!");    
  
}

function getClientIndx(col) {

  var sc = SpreadsheetApp.getActiveSheet();
  var iMax = sc.getLastRow();
  var found = false; 
  
  for(var i=2; i <= iMax; i++) {
    
    if(sc.getRange(i, col).getValue() == true) {
      found = true;
      break;    
    }
 
  }
  
  if(found == true) {
    return i;
  } else {
    Browser.msgBox("Please select at least one client!")
  }

}

function getClientData(indx) {

  var sc = SpreadsheetApp.getActiveSheet();
  var client = {
    name: sc.getRange(indx, 4).getValue(),
    id: sc.getRange(indx, 5).getValue(),
    id_dateOfIssue: sc.getRange(indx, 6).getValue(),
    };
  return client;
}

function getTemlateID(docName) {

  var st = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Templates');
  var iMax = st.getLastRow();
  
  for(var i=2; i <= iMax; i++) {
    
    if(st.getRange(i, 1).getValue() == docName) {
      var docId = st.getRange(i, 2).getValue();
      return docId;    
    }
 
  }

}

Более подробные объяснения о работе скрипта Вы сможете получить, ознакомившись с данным видео:


Продолжнение смотрите в статье:

Генератор документов Google Apps Script с журналом учёта созданных документов.


Читать дальше >>

Список тэгов

    Apps Script      Arrays Java Script      asynchronous code      asyncio      coroutine      Django      Dropdown List      Drop Shipping      Exceptions      GitHub      Google API      Google Apps Script      Google Docs      Google Drive      Google Sheets      multiprocessing      Parsing      Python      regex      Scraping      ssh      Test Driven Development (TDD)      threading      website monitoring      zip