Проблема
Правильно объявленная функция (invoicePrice
) при вызове из другой функции (calcsOnColumns2
) имеет значение undefined
.
Шаги для воспроизведения
Представьте, что у вас есть три функции с именами A
, B
, C
, затем вызов B
вызывает вашу ошибку:
function A() {
var A = 0;
return [A];
}
function C() {
var a = A();
var A = a[0];
}
function B() {
A();
C();
}
Вы, вероятно, заметил, что это то, к чему сводятся ваши функции - поведение связано с подъемом :
- функцией
B
, вызывающей A
, которая возвращает Array
с первым элемент 0
; - [никогда не происходит] функция
C
вызывает функцию A
и переопределяет A
на выход A
;
Вместо этого на втором шаге A
объявляется равным первому элементу a
и только тогда вызывается A
. Поскольку a
получает свое значение от вызова к A
, A
будет undefined
и, как следствие: [undefined]()
-> "не является функцией"
Оптимизация
- Оставайтесь DRY (не повторяйте себя) - когда вы видите код, который выглядит одинаково или имеет ту же структуру, подумайте над тем, чтобы сделать его многократно используемым (сделать его функцией, классом, объектом) переменная - это зависит). Это делает ваш код читабельным и проще в отладке, но большую часть времени экономит вам дополнительные вызовы API, повторные вызовы и, следовательно, время (особенно если вы повторяете ввод-вывод).
- Не используйте комментарии, если только строго необходимо (значительно снижает читабельность - крайне важно, если вам нужны другие, чтобы помочь отладить ваш код или сделать это самостоятельно [может показаться нелогичным, но вы будете удивлены]).
- Не ставьте I / O (
getRange()
, getValue()
) в циклах, если нет выбора - ввод / вывод всегда медленный, поэтому обычно лучше загружать все в память и работать с данными там. - Сохранять согласованную область видимости: если вы объявляете переменную, каждый метод доступа должен находиться либо в одной и той же области видимости, либо во вложенной (например, не объявляйте две переменные с одинаковым именем внутри
if...else
, перемещайте его во внешнюю область видимости). - Используйте TypeScript или по крайней мере JSDo c - удержит вас от сумасшествия, пытаясь понять, почему ваша функция ожидала один тип и получила другой (как в вашем случае) [UPD: я также добавил JSDO * 110 2 * комментарии к решению].
Я применил несколько оптимизаций к вашему сценарию, посмотрите (пожалуйста, проверьте перед использованием, так как у нас нет примеров данных, я не смог Попробуй это). Вы можете сделать гораздо больше (особенно в отношении l oop в calcsOnColumns2
), но это должно быть началом:
/**
* [Your decription here]
* @returns {Number[]}
*/
function invoicePrice() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var subtotalDisplaySheet = ss.getSheetByName('SubtotalDisplay');
// define row to do calculations on
var AVals = subtotalDisplaySheet.getRange("A1:A1000").getValues();
var ALast = AVals.filter(String).length;
// Set order No. here
var orderNoPrev = subtotalDisplaySheet.getRange(ALast - 1, 3).getValue();
var orderNo = subtotalDisplaySheet.getRange(ALast, 3).setValue(orderNoPrev + 1);
// invoicePrice the total number of items
var itemRange = subtotalDisplaySheet.getRange(ALast, 4, 1, 14).getValues()[0];
// set the values of the constants on the Products page
var productsSheet = ss.getSheetByName("Products");
var priceRange = productsSheet.getRange("F1:F1000").getValues();
var pLast = priceRange.filter(String).length;
var pGrab = priceRange.splice(0, pLast);
var outputInvoicePrice = 0;
var weightRange = productsSheet.getRange("V1:V1000").getValues();
var wLast = weightRange.filter(String).length;
var wGrab = weightRange.splice(0, wLast);
var orderAmounts = subtotalDisplaySheet.getRange(Alast, 17).getValues();
var total = 0;
for (var i = 0, len = 13; i <= len; i++) {
var nextI = i + 1;
var orderAmount = orderAmounts[0][i + 4];
total += orderAmount;
var perKGInvoice = pGrab[nextI].map(Number);
var weights = wGrab[nextI].map(Number);
var casePrice = perKGInvoice * weights;
outputInvoicePrice += orderAmount * casePrice;
}
Logger.log(outputInvoicePrice, ALast, pGrab, wGrab);
return [outputInvoicePrice, ALast, pGrab, wGrab];
}
/**
* Gets values from 5 rows in
* a column starting from row 3
* @param {Sheet} sheet
* @param {Number} column
* @returns {*[]}
*/
function getGridValues(sheet, column) {
var range = sheet.getRange(3, column, 5, 1);
return range
.getValues()
.map(function (row) {
return row[0];
});
}
/**
* [Your decription here]
* @returns {Number[]}
*/
function tiers() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var discountGridSheet = ss.getSheetByName("DiscountGrid");
var invoicePriceCall = invoicePrice();
var outputInvoicePrice = invoicePriceCall[0];
var ALast = invoicePriceCall[1];
var beefDiscount = 0;
var sfDiscount = 0;
var currentTier = "";
var tierValues = getGridValues(discountGridSheet, 3);
var tier1 = tierValues[0];
var tier2 = tierValues[1];
var tier3 = tierValues[2];
var tier4 = tierValues[3];
var tier5 = tierValues[4];
var minNotmet = discountGridSheet.getRange(10, 3).getValue();
var tierBeefDiscountValues = getGridValues(discountGridSheet, 4);
var tier1BeefDiscount = tierBeefDiscountValues[0];
var tier2BeefDiscount = tierBeefDiscountValues[1];
var tier3BeefDiscount = tierBeefDiscountValues[2];
var tier4BeefDiscount = tierBeefDiscountValues[3];
var tier5BeefDiscount = tierBeefDiscountValues[4];
var tierSfDiscountValues = getGridValues(discountGridSheet, 5);
var tier1SfDiscount = tierSfDiscountValues[0];
var tier2SfDiscount = tierSfDiscountValues[1];
var tier3SfDiscount = tierSfDiscountValues[2];
var tier4SfDiscount = tierSfDiscountValues[3];
var tier5SfDiscount = tierSfDiscountValues[4];
if (outputInvoicePrice < tier1) {
beefDiscount = tier1BeefDiscount;
sfDiscount = tier1SfDiscount;
currentTier = "Minimum Not Met";
}
else if (outputInvoicePrice < tier2) {
beefDiscount = tier1BeefDiscount;
sfDiscount = tier1SfDiscount;
currentTier = "Tier 1";
}
else if (outputInvoicePrice < tier3) {
beefDiscount = tier2BeefDiscount;
sfDiscount = tier2SfDiscount;
currentTier = "Tier 2";
}
else if (outputInvoicePrice < tier4) {
beefDiscount = tier3BeefDiscount;
sfDiscount = tier3SfDiscount;
currentTier = "Tier 3";
}
else if (outputInvoicePrice < tier5) {
beefDiscount = tier4BeefDiscount;
sfDiscount = tier4SfDiscount;
currentTier = "Tier 4";
}
else {
beefDiscount = tier5BeefDiscount;
sfDiscount = tier5SfDiscount;
currentTier = "Tier 5";
}
var subtotalDisplaySheet = ss.getSheetByName("SubtotalDisplay");
var subtotalDisplayRange = subtotalDisplaySheet.getRange(ALast, 20, 3, 1);
subtotalDisplayRange.setValues([
[beefDiscount],
[sfDiscount],
[currentTier]
]);
return [beefDiscount, sfDiscount, tier1];
}
/**
* Foramts amount string
* @param {Number} amount
* @returns {Function}
*/
function formatAmount(amount) {
return function (discount, caseOrder) {
return amount + " cases @" + discount.toFixed(2) + "/kg = ~$" + caseOrder.toFixed(2);
};
}
/**
* Counts sum by discount, amount and weights
* @param {Number} amount
* @param {Number} weights
* @returns {Number}
*/
function countSum(amount, weights) {
return function (discount) {
return discount * amount * weights;
};
}
/**
* Counts discount case per Kg
* @param {Number} perKG
* @returns {Number}
*/
function countDiscountCase(perKG) {
return function (discount) {
return perKG - (perKG * discount);
};
}
/**
* [Your decription here]
*/
function calcsOnColumns2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var subtotalDisplaySheet = ss.getSheetByName("SubtotalDisplay");
var invoicePriceCall = invoicePrice();
var outputInvoicePrice = invoicePriceCall[0];
var ALast = invoicePriceCall[1];
var priceRange = invoicePriceCall[2];
var weightRange = invoicePriceCall[3];
var tiers = tiers();
var beefDiscount = tiers[0];
var sfDiscount = tiers[1];
var tier1 = tiers[2];
var beefSum = 0;
var sfSum = 0;
var orderAmounts = subtotalDisplaySheet.getRange(ALast, 17).getValues();
for (var i = 0, len = 13; i <= len; i++) {
var orderAmount = orderAmounts[0][i + 4];
var nextI = i + 1;
var perKGInvoice = priceRange[nextI].map(Number);
var weights = weightRange[nextI].map(Number);
var casePrice = perKGInvoice * weights;
var subtotalRange = subtotalDisplaySheet.getRange(ALast, i + 32);
var formatAmountOrder = formatAmount(orderAmount);
var countDisountSum = countSum(orderAmount, weights);
var countPerKGCase = countDiscountCase(perKGInvoice);
// Split up seafood and beef with if statement:
if (i < 9) {
var discountCaseBeef = countPerKGCase(beefDiscount);
var beefCaseOrder = countDisountSum(discountCaseBeef);
beefSum += beefCaseOrder;
} else {
var discountCaseSF = countPerKGCase(sfDiscount);
var sfCaseOrder = countDisountSum(discountCaseSF);
sfSum += sfCaseOrder;
}
if (orderAmount) {
subtotalRange.setValue(
i < 9 ?
formatAmountOrder(discountCaseBeef, beefCaseOrder) :
formatAmountOrder(discountCaseSF, sfCaseOrder)
);
}
if (i == len) {
subtotalDisplaySheet.getRange(ALast, 24).setValue(beefSum);
subtotalDisplaySheet.getRange(ALast, 25).setValue(sfSum);
// if statement to go here if min order size not met
var subtotalRange = subtotalDisplaySheet.getRange(ALast, 27);
var sumOfSums = beefSum + sfSum;
subtotalRange.setValue(sumOfSums > tier1 ? "Minimum order size not met" : sumOfSums);
var totalDiscount = outputInvoicePrice - sumOfSums;
subtotalDisplaySheet.getRange(ALast, 29).setValue(totalDiscount);
subtotalDisplaySheet.getRange(ALast, 30).setValue(outputInvoicePrice);
}
}
}
Примечания
- Хотя современный JavaScript поможет вам избежать этой проблемы, вы никогда не должны переопределять имена переменных если вы точно не знаете, почему вы это делаете (например,
i++
, однако функциональный стиль скажет, что даже это проблематично c). - Никогда не обрезайте сообщения об ошибках / код при публикации в SO для ради читабельности - знание того, что именно произошло, неоценимо для нас, чтобы помочь вам (
invoicePrice
- это undefined
говорит о многом другом).
Ссылки
- Подъем in JavaScript