Суммарное произведение через Google Script PT2

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

Вот образец документа. В этом документе всего 5 вкладок:

  1. Набор данных до — так будет выглядеть набор данных перед обработкой.
  2. Результат набора данных (старый) — так выглядит набор данных после обработки текущим скриптом.
  3. Результат набора данных (новое требование) — так должен выглядеть набор данных после добавления моих новых требований.
  4. Данные о завершении — это данные, которые определяют статус завершения.
  5. Настройки. На этом листе будут сопоставлены различные коды курсов для каждого курса.

Вот что у меня есть на данный момент:

  // This is from your provided Spreadsheet.
  const obj = [
    { name: "Dataset Before", range: "A2:A" },
    { name: "Completion Data", range: "A2:C" },
    { name: "Settings", range: "E5:I" }
  ];

  // 1. Retrieve values and ranges from 3 sheets.
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const [sheet1, sheet2, sheet3] = obj.map(({ name, range }, i) => {
    obj[i].sheet = ss.getSheetByName(name);
    obj[i].range = obj[i].sheet.getRange(range + obj[i].sheet.getLastRow());
    obj[i].values = obj[i].range.getValues();
    if (i == 1) {
      // For "Completion Data" sheet.
      // Create an object for searching values.
      obj[i].values = obj[i].values.reduce((o, [a, b, c]) => {
        if (c == "Completed") {
          o[b] = o[b] ? { ...o[b], [a]: c } : { [a]: c };
        }
        return o;
      }, {});
    } else if (i == 2) {
      // For "Settings" sheet.
      // Transpose the retrieved values.
      obj[i].len = obj[i].values[0].length;
      obj[i].values = obj[i].values[0].map((_, col) => obj[i].values.map((row) => row[col]));
    }
    return obj[i];
  });

  // Create an array for putting into the destination range by follwoing your logic.
  const values = sheet1.values.map(([a]) => {
    if (sheet2.values[a]) {
      return sheet3.values.map(e => e.some(f => sheet2.values[a][f]) ? "Completed" : "Incomplete");
    }
    return Array(sheet3.len).fill("Incomplete");
  });

  // Put the values to the destination range.
  sheet1.sheet.getRange(2, 2, values.length, values[0].length).setValues(values);
}

В настоящее время сценарий делает то, что я хочу, и делает это примерно за 40 секунд. Действительно быстро по сравнению с тем, что у меня было раньше. Он ищет идентификатор человека на вкладке «Данные о завершении» и возвращает «Завершено», если в статусе указано «Завершено», или «Незавершено», если возвращается какой-либо другой статус.

Новое требование: Теперь мне нужно, чтобы он вернул дату завершения, объединенную со словом «Завершено», например Completed (04/24/2024), или вернул Incomplete (Enrolled), если статус показывает «Зарегистрирован», в противном случае просто Incomplete.

К сожалению, я не до конца понимаю, что происходит в этом сценарии, чтобы внести нужные мне изменения. Я пытался создать что-то с нуля самостоятельно, используя имеющиеся знания, но это включало циклы внутри циклов и занимало более 15 минут на каждый столбец. Время выполнения не позволило бы всему этому закончиться.

При попытке изменить приведенный выше код я предположил, что нужно внести изменения в return sheet3.values.map(e => e.some(f => sheet2.values[a][f]) ? "Completed" : "Incomplete");, но я продолжал получать ошибки. На данный момент это слишком сложно для меня, и я был бы очень признателен за помощь.

Пожалуйста, дайте мне знать, если я что-то упустил или что-то неясно, чтобы я мог отредактировать свое сообщение. Спасибо.

@Tanaike Отмечаю человека, который предложил мне создать новый вопрос.

DanCue 25.04.2024 01:13

Около @Tanaike Tagging person who suggested I create a new question. на листе «Результат набора данных (новое требование)» в столбцах от «C» до «E» указано только «Неполный». Могу ли я спросить вас о причине этого? Во-первых, мне хотелось бы правильно понять Ваш вопрос.

Tanaike 25.04.2024 04:13

Я только что убедился, что это правда. В столбцах C–E указано «Незавершено», поскольку у этих курсов нет статуса. Если в статусе указано «Завершено», должно быть указано «Завершено (мм/дд/гггг)». Если в статусе указано «Зарегистрировано», должно быть написано «Незавершено (Зарегистрировано)». Все остальное или недостающее должно быть отмечено как «Не завершено».

DanCue 25.04.2024 13:23

Я добавил еще несколько строк для примеров.

DanCue 25.04.2024 13:39

Спасибо за ответ. Когда я увидел обновленную таблицу, я не мог понять, почему 6878682316 есть Completed (08/15/2023),Completed (08/13/2023),Incomplete,Incomplete (Enrolled),Completed (08/24/2023). Я догадалась, что это может быть Incomplete,Incomplete,Incomplete,Incomplete,Incomplete. Могу ли я спросить вас о деталях? Кстати, ваша просьба в этом вопросе - добиться только «Результат набора данных (новое требование)» из листов «Набор данных до», «Данные завершения» и «Настройки»? Во-первых, мне хотелось бы правильно понять Ваш вопрос. Я прошу прощения за эту ситуацию.

Tanaike 25.04.2024 14:13

Каждый идентификатор может иметь одну запись для каждого курса. Если вы отфильтруете номер 6878682316 на вкладке «Данные о завершении», вы увидите, что у них есть статус для каждого курса. Я обновил «Данные о завершении», чтобы это соответствовало этому идентификатору. Если вы скопировали его ранее, замените его новыми данными. Что касается второго вопроса, вы правы... «Результат набора данных (новое требование)» — это то, чего я пытаюсь достичь с помощью других вкладок, которые вы упомянули. И не надо извиняться. Ты обалденный. Спасибо, что изучили это.

DanCue 25.04.2024 14:29

Спасибо за ответ. Что касается If you filter for 6878682316 on "Completion Data" tab you will see that they have a status for each course. I updated the "Completion Data" so that would be the case for this ID., когда я проверил 6878682316 на листе «Данные о завершении», никаких значений обнаружено не было. Я обеспокоен тем, что ваша ситуация может отличаться от предоставленной вами таблицы. Но я хотел бы это подтвердить. Когда я повторяю это, мне хотелось бы подумать о решении.

Tanaike 25.04.2024 15:00

Я понимаю. Мои извинения. Я присвоил этот статус не тому человеку. Ты прав. Все должно быть незавершенным. Я только что внес изменения, чтобы этот идентификатор соответствовал сценарию, который я собирался использовать. Пожалуйста, скопируйте вкладку данных завершения еще раз.

DanCue 25.04.2024 15:06

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

DanCue 25.04.2024 19:05

Спасибо за ответ. Из вашего ответа я предложил модифицированный сценарий. Пожалуйста, подтвердите это.

Tanaike 26.04.2024 01:13
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
10
73
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В вашей ситуации, в качестве простой модификации, как насчет следующей модификации?

Модифицированный скрипт:

function myFunction() {
  // This is from your provided Spreadsheet.
  const obj = [
    { name: "Dataset Before", range: "A2:A" },
    { name: "Completion Data", range: "A2:D" },
    { name: "Settings", range: "E5:I" }
  ];

  // 1. Retrieve values and ranges from 3 sheets.
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const [sheet1, sheet2, sheet3] = obj.map(({ name, range }, i) => {
    obj[i].sheet = ss.getSheetByName(name);
    obj[i].range = obj[i].sheet.getRange(range + obj[i].sheet.getLastRow());
    obj[i].values = obj[i].range.getDisplayValues();
    if (i == 1) {
      // For "Completion Data" sheet.
      // Create an object for searching values.
      obj[i].values = obj[i].values.reduce((o, [a, b, c, d]) => {
        if (c == "Completed") {
          o[b] = o[b] ? { ...o[b], [a]: `${c} (${d})` } : { [a]: `${c} (${d})` };
        } else if (c == "Enrolled") {
          o[b] = o[b] ? { ...o[b], [a]: `Incomplete (${c})` } : { [a]: `Incomplete (${c})` };
        }
        return o;
      }, {});
    } else if (i == 2) {
      // For "Settings" sheet.
      // Transpose the retrieved values.
      obj[i].len = obj[i].values[0].length;
      obj[i].values = obj[i].values[0].map((_, col) => obj[i].values.map((row) => row[col]));
    }
    return obj[i];
  });

  // Create an array for putting into the destination range by following your logic.
  const values = sheet1.values.map(([a]) => {
    if (sheet2.values[a]) {
      return sheet3.values.map(e => {
        const k = e.find(f => sheet2.values[a][f])
        return k ? sheet2.values[a][k] : "Incomplete";
      });
    }
    return Array(sheet3.len).fill("Incomplete");
  });

  // Put the values to the destination range.
  sheet1.sheet.getRange(2, 2, values.length, values[0].length).setValues(values);
}

Тестирование:

Когда этот сценарий запускается в предоставленной вами электронной таблице, на листе «Набор данных до» получается следующий результат.

Добавлен

Судя по вашему следующему ответу,

Это увеличило время обработки примерно на 20 секунд (~ 70 000 строк в наборе данных, ~ 150 000 в данных завершения) в общей сложности на 1 минуту 10 секунд... но это не имеет большого значения.

В качестве простой модификации, как насчет замены getValues и serValues на Sheets API следующим образом?

Прежде чем использовать этот скрипт, включите Sheets API в расширенных сервисах Google.

function myFunction() {
  // This is from your provided Spreadsheet.
  const obj = [
    { name: "Dataset Before", range: "A2:A" },
    { name: "Completion Data", range: "A2:D" },
    { name: "Settings", range: "E5:I" }
  ];

  // 1. Retrieve values and ranges from 3 sheets.
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const ssId = ss.getId();
  const [sheet1, sheet2, sheet3] = obj.map(({ name, range }, i) => {
    obj[i].sheet = ss.getSheetByName(name);
    obj[i].range = obj[i].sheet.getRange(range + obj[i].sheet.getLastRow());

    // obj[i].values = obj[i].range.getDisplayValues();
    obj[i].values = Sheets.Spreadsheets.Values.get(ssId, `'${name}'!${range}`, { valueRenderOption: "FORMATTED_VALUE" }).values;

    if (i == 1) {
      // For "Completion Data" sheet.
      // Create an object for searching values.
      obj[i].values = obj[i].values.reduce((o, [a, b, c, d]) => {
        if (c == "Completed") {
          o[b] = o[b] ? { ...o[b], [a]: `${c} (${d})` } : { [a]: `${c} (${d})` };
        } else if (c == "Enrolled") {
          o[b] = o[b] ? { ...o[b], [a]: `Incomplete (${c})` } : { [a]: `Incomplete (${c})` };
        }
        return o;
      }, {});
    } else if (i == 2) {
      // For "Settings" sheet.
      // Transpose the retrieved values.
      obj[i].len = obj[i].values[0].length;
      obj[i].values = obj[i].values[0].map((_, col) => obj[i].values.map((row) => row[col]));
    }
    return obj[i];
  });

  // Create an array for putting into the destination range by follwoing your logic.
  const values = sheet1.values.map(([a]) => {
    if (sheet2.values[a]) {
      return sheet3.values.map(e => {
        const k = e.find(f => sheet2.values[a][f])
        return k ? sheet2.values[a][k] : "Incomplete";
      });
    }
    return Array(sheet3.len).fill("Incomplete");
  });

  // console.info(values)

  // Put the values to the destination range.
  // sheet1.sheet.getRange(2, 2, values.length, values[0].length).setValues(values);
  Sheets.Spreadsheets.Values.update({ values }, ssId, `'${sheet1.name}'!B2`, { valueInputOption: "USER_ENTERED" });
}

Это сделало это. Это увеличило время обработки примерно на 20 секунд (~ 70 000 строк в наборе данных, ~ 150 000 в данных завершения) в общей сложности на 1 минуту 10 секунд... но это не имеет большого значения. Определенно стоит потраченного времени по сравнению с альтернативным решением, которое у меня было. Спасибо!

DanCue 26.04.2024 01:31

@DanCue Спасибо за ответ. Из вашего ответа я добавил еще один скрипт. Пожалуйста, проверьте это еще раз. Я думаю, что в этом случае стоимость процесса можно будет немного снизить.

Tanaike 26.04.2024 01:58

Ты быстрый. Я почти сразу удалил свой комментарий, потому что понял это. Да, я заменил ваш values на shtValues.

DanCue 26.04.2024 02:48

Отличный! Это сократило время вдвое. Довольно круто, спасибо!

DanCue 26.04.2024 03:04

На самом деле это лучше, чем я думал. 13 секунд. Я понял, что все еще вызываю полный набор данных с помощью getValues в другой строке, которую использовал для старого сценария. Избавился от этого, и эта штука работает молниеносно.

DanCue 26.04.2024 03:30

Другие вопросы по теме