Bir aralığı Google Spreadsheets'te özel bir işleve nasıl geçirebilirim?


44

Menzili olan bir fonksiyon yaratmak istiyorum ... şunun gibi:

function myFunction(range) {
  var firstColumn = range.getColumn();
  // loop over the range
}

Bir hücre aşağıdakileri kullanarak başvuruda bulunabilir:

=myFunction(A1:A4)

Sorun şu ki, bunu yapmaya çalıştığımda, parametre sadece fonksiyona değerleri iletiyor gibi görünüyor. Böylece, ben herhangi kullanamazsınız Menzil yöntemlerle gibi getColumn(). Bunu yapmaya çalıştığımda, aşağıdaki hatayı veriyor:

error: TypeError: 1,2,3 nesnesindeki getColumn işlevi bulunamıyor.

Özel işlevlerimden birine yalnızca değerler yerine gerçek bir aralığı nasıl gönderebilirim?

Yanıtlar:


22

Bu yüzden uzun ve çok bu aramaya iyi bir cevap aradım ve işte bulduklarım:

  1. değiştirilmemiş bir aralık parametresi , aralığın içindeki hücrelerin değerlerinden geçer , örneğin aralığın kendisinden değil (Gergely'nin açıkladığı şekilde) örneğin: bunu yaptığınızda=myFunction(a1:a2)

  2. fonksiyonunuzda bir aralık kullanmak için önce aralığı bir dizge olarak geçirmeniz gerekir (örneğin:) =myFunction("a1:a2"), ardından fonksiyonun içinde aşağıdaki kodla bir aralığa dönüştürün:

    Function myFunction(pRange){
      var sheet = SpreadsheetApp.getActiveSpreadsheet();
      var range = sheet.getRange(pRange);
    }
    
  3. Son olarak, bir aralıkta geçiş yapmanın faydalarını istiyorsanız (aralık referanslarını diğer hücrelere akıllıca kopyalama gibi) VE onu bir dizge olarak geçirmek istiyorsanız, bir diziyi parametre olarak kabul eden ve bir çıktı veren bir işlev kullanmanız gerekir. Kullanabileceğiniz dize. Aşağıda bulduğum her iki yolu da detaylandırıyorum, ancak 2. olanı tercih ediyorum.

    Tüm google e-tablolar için: =myFunction(ADDRESS(row(A1),COLUMN(A1),4)&":"&ADDRESS(row(A2),COLUMN(A2),4));

    Yeni Google Sayfaları için: =myFunction(CELL("address",A1)&":"&CELL("address",A2));


"Aralık referanslarını diğer hücrelere akıllıca kopyalamanın" ne anlama geldiğini açıklayabilir misiniz?
Emerson Farrugia,

Cankurtaran. Çalışıyor, teşekkür ederim.
Jithin Pavithran

@EmersonFarrugia İşlevleri bir hücreden diğerine kopyaladığınızda google sayfalarının göreceli hücre başvurularını otomatik olarak güncelleme becerisine atıfta bulunuyorum
Nathan Hanna

9

rangejavascript'in 2d dizisi olarak değerlendirilir. Ile birlikte satır range.lengthsayısını ve ile birlikte sütun sayısını alabilirsiniz range[0].length. Eğer satır değeri elde etmek istiyorsanız rve sütun ckullanımı: range[r][c].


Aralıktaki ilk sütunun ne olduğunu bulmaya çalışıyorum. Örneğin, aralık C1: F1, bilmek isteyen bu sütun C ile yelpazesi başlar
Senseful

Eğer böyle bir hücrede fonksiyonunu kullanacağız @Senseful ise: =myFunction(C1:F1), daha sonra işlevi içinde range[0][0]değerini döndürür C1, range[0][1]değerini döndürecektir D1, range[0][2]değerini döndürecektir E1vb .. Anlaşıldı mı?
Lipis,

Sanırım kendimi açıkça açıklamıyorum ... bu sorunun cevabına bakın . Sen benim kullanmamı =weightedAverage(B3:D3,$B$2:$D$2)önerdin, 2. argümanı göndermek zorunda kalmayarak (yani olarak çağırmak istiyorum =weightedAverage(B3:D3)) fonksiyonu daha hataya dayanıklı hale getirmek istiyorum , ve kodun, aralığın B ile başladığını ve bitip biteceğini otomatik olarak bilmesini istiyorum. D, böylece 2. satırdan ilgili değerleri alır. Not: "2" değeri kodlanmış olabilir, ancak "B" kodlanmış olmamalıdır.
Duygusal

1
@Senseful Genel olarak kodlanmış konulara karşıyım ve ağırlıklı ortalamanın iki paragraf alacağı daha açık olduğunu düşünüyorum. Eğer zor kodlar üzereseyseniz, yapabileceğiniz birçok şey var. Şu an bulamıyorum, Bverilenlerden nasıl çıkacağız range. Size henüz başlamadıysanız , aralıklar ve hücreler ile nasıl oynayabileceğiniz hakkında bir fikir edinmek için başlangıç kılavuzuna ( goo.gl/hm0xT ) geçmenizi öneririm .
Lipis

6

Bir RangeGoogle elektronik tablo işlevine geçerken , çerçeve paramRange.getValues()örtük olarak yürütülür ve işleviniz Aralıktaki değerleri 2 boyutlu dizeler, sayılar veya nesneler dizisi (benzeri Date) olarak alır. RangeNesne özel tablo işlevine aktarılmaz.

Aşağıdaki TYPEOF()fonksiyon size parametre olarak ne tür veriler aldığınızı gösterecektir. Formül

=TYPEOF(A1:A4)

betiği şöyle çağırır:

calculateCell () {
  var resultCell = SpreadsheetApp.getActiveSheet (). getActiveCell ();
  var paramRange = SpreadsheetApp.getActiveSheet (). getRange ('A1: A4');
  var paramValues ​​= paramRange.getValues ​​();

  var resultValue = TYPEOF (paramValues);

  resultCell.setValue (resultValue);
}
İşlev TYPEOF (değer) {
  if (typeof value! == 'nesne')
    typeof değerini döndür;

  var details = '';
  if (dizi değeri)
    detaylar + = '[' + value.length + ']';
    if (değer [0] Array)
      detaylar + = '[' + değer [0]. uzunluk + ']';
  }

  var className = value.constructor.name;
  className + ayrıntıları döndür;
}

3

İlham bir alternatif olarak yorum tarafından @Lipis gelen, ek parametreler olarak satır / sütun koordinatları ile sizin işlevini çağırabilirsiniz:

=myFunction(B1:C2; ROW(B1); COLUMN(B1); ROW(C2); COLUMN(C2))

Bu formda, formül kolayca diğer hücrelere kopyalanabilir (veya sürüklenebilir) ve hücre / aralık referansları otomatik olarak ayarlanacaktır.

Script işlevinizin şu şekilde güncellenmesi gerekir:

function myFunction(rangeValues, startRow, startColumn, endRow, endColumn) {
  var range = SpreadsheetApp.getActiveSheet().getRange(startRow, startColumn, endRow - startRow + 1, endColumn - startColumn + 1);
  return "Range: " + range.getA1Notation();
}

Not bu getRangefonksiyon alır startRow, startColumnve daha sonra sayıda satır ve sayısı sütun değil - son satır ve son sütun. Böylece, aritmetics.


Beklediğim cevap bu. SATIR ve KOLON.
Jianwu Chen,

3

Bu yapılabilir . Biri, formülü içeren hücre olan aktif hücrede formülü ayrıştırmak suretiyle geçen aralığa bir referans alabilir. Bu, özel fonksiyonun kendi başına ve daha karmaşık bir ifadenin parçası olarak kullanılmadığı varsayımına sahiptir: örneğin =myfunction(A1:C3), değil =sqrt(4+myfunction(A1:C3)).

Bu işlev geçirilen aralığın ilk sütun dizinini döndürür.

function myfunction(reference) {
  var sheet = SpreadsheetApp.getActiveSheet();
  var formula = SpreadsheetApp.getActiveRange().getFormula();
  var args = formula.match(/=\w+\((.*)\)/i)[1].split('!');
  try {
    if (args.length == 1) {
      var range = sheet.getRange(args[0]);
    }
    else {
      sheet = ss.getSheetByName(args[0].replace(/'/g, ''));
      range = sheet.getRange(args[1]);
    }
  }
  catch(e) {
    throw new Error(args.join('!') + ' is not a valid range');
  }

  // everything so far was only range extraction
  // the specific logic of the function begins here

  var firstColumn = range.getColumn();  // or whatever you want to do with the range
  return firstColumn;
}

Bu yazının aynı zamanda Yığın Taşması ile ilgili şu soruya da cevap verdiğini düşünüyorum : hücre referanslarının elektronik tablo işlevlerine geçirilmesi
Rubén

2

Kullanıcının79865'in yanıtındaki mükemmel fikri, daha fazla vaka için çalışmasını sağlamak ve özel işleve birden çok argümanla geçmek üzere genişlettim .

Kullanmak için, aşağıdaki kodu kopyalayın ve ardından özel işlev çağrınızda bunun GetParamRanges()gibi, işlev adınızı iletin:

function CustomFunc(ref1, ref2) {

  var ranges = GetParamRanges("CustomFunc"); // substitute your function name here

  // ranges[0] contains the range object for ref1 (or null)
  // ranges[1] contains the range object for ref2 (or null)

  ... do what you want

  return what_you_want;
}

İşte bir hücrenin rengini döndürmek için bir örnek:

/**
* Returns the background color of a cell
*
* @param {cell_ref} The address of the cell
* @return The color of the cell
* @customfunction
*/
function GetColor(ref) {

  return GetParamRanges("GetColor")[0].getBackground();
}

İşte kod ve biraz daha açıklama:

/**
* Returns an array of the range object(s) referenced by the parameters in a call to a custom function from a cell
* The array will have an entry for each parameter. If the parameter was a reference to a cell or range then 
* its array element will contain the corresponding range object, otherwise it will be null.
*
* Limitations:
* - A range is returned only if a parameter expression is a single reference.
*   For example,=CustomFunc(A1+A2) would not return a range.
* - The parameter expressions in the cell formula may not contain commas or brackets.
*   For example, =CustomFunc(A1:A3,ATAN2(4,3),B:E) would not parse correctly.
* - The custom function may not appear more than once in the cell formula.
* - Sheet names may not contain commas, quotes or closing brackets.
* - The cell formula may contain white space around the commas separating the custom function parameters, or after
*   the custom function name, but not elsewhere within the custom function invocation.
* - There may be other limitations.
* 
* Examples:
*   Cell formula: =CUSTOMFUNC($A$1)
*   Usage:        var ranges = GetParamRanges("CustomFunc");
*   Result:       ranges[0]: range object for cell A1 in the sheet containing the formula
*
*   Cell formula: =CUSTOMFUNC(3, 'Expenses'!B7)
*   Usage:        var ranges = GetParamRanges("CustomFunc");
*   Result:       ranges[0]: null
*                 ranges[1]: range object for cell B7 in sheet Expenses
* 
*   Cell formula: =sqrt(4+myfunction(A1:C3))
*   Usage:        var ranges = GetParamRanges("MyFunction");
*   Result:       ranges[0]: range object for cells A1 through C3 in the sheet containing the formula
*
*   Cell formula: =CustomFunc(A1+A2, A1, A2)
*   Usage:        var ranges = GetParamRanges("CustomFunc");
*   Result:       ranges[0]: null
*                 ranges[1]: range object for cell A1 in the sheet containing the formula
*                 ranges[2]: range object for cell A2 in the sheet containing the formula
*
* @param {funcName} The name of the custom function (string, case-insensitive)
* @return The range(s) referenced by the parameters to the call
*/
function GetParamRanges(funcName) {
  var ourSheet = SpreadsheetApp.getActiveSheet();
  var formula = SpreadsheetApp.getActiveRange().getFormula();
  var re = new RegExp(".+" + funcName + "\\s*\\((.*?)\\)","i");
  var ranges=[]; // array of results

  try {
    var args = formula.match(re)[1].split(/\s*,\s*/) // arguments to custom function, separated by commas
    // if there are no args it fails and drops out here

    for (var i=0; i<args.length; i++) {
      var arg=args[i].split('!'); // see if arg has a sheet name
      try {
        if (arg.length == 1) { // if there's no sheet name then use the whole arg as the range definition
          ranges[i] = ourSheet.getRange(arg[0]);
        }
        else { // if there's a sheet name, use it (after removing quotes around it)
          var sheetName=arg[0].replace(/'/g, '');
          var otherSheet=SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
          ranges[i] = otherSheet.getRange(arg[1]);
        }
      }
      catch(e) { // assume it couldn't be identified as a range for whatever reason
        ranges[i]=null;
      }
    }
  }
  catch(e) {}

  return ranges
}

1
hiç de fena değil, ancak lütfen bunun bir varsayım fonksiyonunun bir formülde sadece bir kez kullanıldığını varsaydığını söyleyin. Fi şu forumları kullanmalıyım: = CountCcolor (B5: B38, $ A40) / 2 + CountCcolor (B5: B38, $ A41) / 2 + CountCcolor (B5: B38, $ A42) / 2 + CountCcolor (B5: B38 , $ A43) / 2 + CountCcolor (B5: B38, $ A44) / 2 Hangi şekilde anladım, bu şekilde çalışmayacak. Bunu çözersek, bunun üstesinden gelebilirsek harika olur.
Eylül’de

1

Google E-Tablo'da, Google Apps Komut Dosyası aracılığıyla iki farklı özel işlevi ayırt ediyorum:

  1. Bir API için çağrıyorum; Elektronik Tablo Uygulaması ( örnek )
  2. Arama yapmayan biri ( örnek )

İlk işlev hemen hemen her şeyi yapabilir. Elektronik Tablo servisini çağırmanın yanı sıra, GmailApp'ı veya Google tarafından sağlanan herhangi bir hizmeti de arayabilir. API çağrıları işlemi yavaşlatır. Mevcut olan tüm yönteme erişmek için bir aralık üzerinden geçirilebilir veya işlevden çıkarılabilir.

İkinci işlev yalnızca "Elektronik Tablo" ile sınırlandırılmıştır. Burada biri verileri yeniden işlemek için JavaScript’ten faydalanabilir. Bu fonksiyonlar normalde çok hızlıdır. Bir aralık geçti, 2B dizi içeren değerlerden başka bir şey değil.


Sorunuzda, .getColumn()yöntemi arayarak başlayın . Bu açıkça yukarıda açıklandığı gibi bir tip 1 özel işleve ihtiyacınız olduğunu gösterir.

Özel bir işlev oluşturmak için elektronik tablonun ve sayfanın nasıl ayarlanacağı ile ilgili aşağıdaki cevaba bakın .


1

Birkaç ay önce bunun üzerinde çalışıyordum ve çok basit bir tortu ile karşılaştım: içeriği olarak her hücrenin adını taşıyan yeni bir sayfa oluşturun: A1 hücresi şöyle görünebilir:

= arrayformula(cell("address",a1:z500))

Sayfayı "Ref" olarak adlandırın. Ardından, içerik yerine bir dizge olarak bir hücreye başvuru gerektiğinde, şunları kullanırsınız:

= some_new_function('Ref'!C45)

Elbette, fonksiyonun bir dizgiden (bir hücre) veya bir 1D veya 2D Dizilimden geçip geçmediğini kontrol etmeniz gerekir. Bir dizi alırsanız, tüm hücre adreslerini dizge olarak alacaktır, ancak ilk hücreden ve genişlik ve yükseklikten ihtiyacınız olanı çözebilirsiniz.


0

Bütün sabah bunun üzerinde çalıştım ve yukarıdaki cevapların ne olduğunu tartıştığını gösteren kanıtlar gördüm. Mantıklı ve her ikisi de değeri değil, geçen hücrenin ADRESİ'ni istiyorum. Ve cevap çok kolaydır. Bu yapılamaz.

Hangi hücrenin formülü içerdiğini bulmaya dayanan bazı geçici çözümler buldum. Bunun yukarıdaki Mantıklı'ya yardımcı olup olmadığını söylemek zor. Peki ben ne yapıyordum?

The data.

     [A]      [B]       [C]       [D]     [E]     [F]       [H]
[1] Name      Wins     Losses    Shots   Points   Fouls   Most recent
                                                          WL Ratio
[2] Sophia     4         2         15      7       1         0
[3] Gloria     11        3         11      6       0         0
[4] Rene       2         0         4       0       0         0
[5] Sophia     7         4         18      9       1         1.5

H Sütunu Sophia'nın (Kazananlar - PrevWins) / (Kayıplar - PrevLosses)

(7 - 4) / (4-2) = 1.5

Fakat Sophia'nın daha önce hangi sırada göründüğünü bilmiyoruz.
Bu, isim sütunu olarak A kodunu girerseniz VLOOKUP kullanılarak yapılabilir. VLOOKUP'tan sonra bazı durumlarda #NA (isim bulunamadı) ve # DIV0 (sıfır değeri) alıyordum ve bu koşullarda daha lezzetli metinler göstermek için = IF (IF (...)) ile sardım. Şimdi hantal ve sürdürülemeyen bir canavarca büyük ifade vardı. Bu yüzden makro genişlemesi (mevcut değil) veya özel fonksiyonlar istedim.

Ama bir yardımcı SubtractPrevValue (hücre) yaptığımda, B5 yerine "7" alıyordu. İletilen bağımsız değişkenlerden hücre veya aralık nesneleri almanın yerleşik bir yolu yoktur.
Kullanıcının hücre adını çift tırnak işareti ile girmesini sağlarsam, bunu yapabilirim ... SubtractPrevValue ("B5"). Ama bu gerçekten hamstrings kopyala / yapıştır ve bağıl hücre özellikleri.

Ama sonra bir geçici çözüm buldum.

SpreadsheetApp.getActiveRange () formülü içeren hücredir. Bilmem gereken tek şey bu. Satır numarası Aşağıdaki işlev bir NUMERIC sütun numarası alır ve bu sütunda önceki oluşumunu çıkarır.

function SubtractPrevValue(colNum) 
{
  var curCell = SpreadsheetApp.getActiveRange();
  var curSheet = curCell.getSheet();
  var curRowIdx = curCell.getRowIndex();
  var name = curSheet.getRange(curRowIdx, 1).getValue();  // name to match
  var curVal =  curSheet.getRange(curRowIdx, colNum).getValue();

  var foundRowIdx = -1;
  for (var i=curRowIdx-1;i>1;i--)
  { 
    if (curSheet.getRange(i, 2).getValue() == name)
    {
      return curVal - curSheet.getRange(i, colNum).getValue();
    }
  }
  return curVal;  //default if no previous found
}

Ama sonra bunun gerçekten gerçekten yavaş olduğunu keşfettim. "Düşünme ..." ifadesini görüntülerken bir veya iki saniye gecikme var, bu yüzden büyük ölçüde okunaksız, sürdürülemez bir çalışma sayfası formülüne geri döndüm.


0

"Range" nesnesini sağlamak yerine, Google Geliştiricileri hemen hemen rastgele bir veri türü iletir. Böylece girişin değerini yazdırmak için aşağıdaki makroyu geliştirdim.

function tp_detail(arg)
{
    var details = '';

    try
    {
        if(typeof arg == 'undefined')
           return details += 'empty';

        if (typeof arg !== 'object')
        {
            if (typeof arg == 'undefined')
                return details += 'empty';

            if (arg.map)
            {
                var rv = 'map: {';
                var count = 1;
                for (var a in arg)
                {
                    rv += '[' + count + '] ' + tp_detail(a);
                    count = count + 1;
                }
                rv += '}; '
                return rv;
            }
            return (typeof arg) + '(\'' + arg + '\')';
        }


        if (arg instanceof Array)
        {
            details += 'arr[' + arg.length + ']: {';
            for (var i = 0; i < arg.length; i++)
            {
                details += '[' + i + '] ' + tp_detail(arg[i]) + ';';
            }
            details += '} ';
        }
        else
        {

            var className = arg.constructor.name;
            return className + '(' + arg + ')';
        }
    }
    catch (e)
    {
        var details = '';
        details = 'err : ' + e;
    }


    return details;
}

0

Benim önceki cevapların derlenmesi var

**
 *
 * @customfunction
 **/
function GFR(){
  var ar = SpreadsheetApp.getActiveRange();
  var as = SpreadsheetApp.getActive();
  return ar.getFormula()
    .replace(/=gfr\((.*?)\)/i, '$1')
    .split(/[,;]/)
    .reduce(function(p, a1Notation){
      try{
        var range = as.getRange(a1Notation);
        p.push(Utilities.formatString(
          'Is "%s" a Range? %s', 
          a1Notation,
          !!range.getDisplayValues
        ));
      } catch(err){
        p.push([a1Notation + ' ' + err.message]);
      }
    return p;
  },[]);
}

Geçerli formülü alır ve bir Aralık olup olmadığını kontrol eder.

=GFR(A1:A5;1;C1:C2;D1&D2) döner

|Is "A1:A5" a Range? true|
|1 Range not found       |
|Is "C1:C2" a Range? true|
|D1&D2 Range not found   |

Kompozisyonun tamamı için TC buna benzer bir şey arayabilir

var range = as.getRange(a1Notation);
var column = range.getColumn();

-1

İşlev oluşturun ve aralığı argüman olarak iletin:

function fn(input) {
    var cell = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange(SpreadsheetApp.getActiveRange().getFormula().match(/=\w+\((.*)\)/i)[1].split('!'));
    // above ... cell is "range", can convert to array?
    var sum=0;
    for (var i in cell.getValues() ){
        sum = sum + Number(cell.getValues()[i]);
    }  
    return sum;
}

Elektronik tabloda kullanım:

=fn(A1:A10)

Olarak değerini gösterecektir sum(A1:A10).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.