1

Problem:

I'm trying to add each ordinal reference to a set of repeating values in each cells just above each value.

The values are organized in horizontal and non-contiguous order.

The illustration example I show below is simple for testing purposes, but the end use should be for hundreds of values/ranges, so it would be optimal to use a script or a simplified version of the formula I found.

Illustration Example:

Ordinals from Non-Contiguous Ranges

Other Related Question and Solution:

I found that question and answers that address the same question but for vertical and contiguous values using the following formula as solution:

=COUNTIF(A$1:A1,A1)

=COUNTIF(A$1:A1,A1)&MID("thstndrdth",MIN(9,2*RIGHT(COUNTIF(A$1:A1,A1))*(MOD(COUNTIF(A$1:A1,A1)-11,100)>2)+1),2)

Calculate ordinal number of replicates

My Formula So Far:

=TRANSPOSE(INDIRECT($P$21&(SUM(Q21))&":"&$P$21&(SUM(Q21,I22)-1)))

=TRANSPOSE(INDIRECT($P$21&(SUM(Q21,I22))&":"&$P$21&(SUM(Q21,I22,I26)-1)))

=TRANSPOSE(INDIRECT($P$21&(SUM(Q21,I22,I26))&":"&$P$21&(SUM(Q21,I22,I26,I30)-1)))

I use the above formula and need to copy-paste it in the cell immediately above the 1st cell of each horizontal range.

I need to reference each cell in the SUM Functions part because the spreadsheet will act as a template, with new data sets that will be different each time.

Therefore the cells need to return output in some dynamic way (can't hardcode them).

The formula problem is it requires an ever growing number of cells reference as we get to new ranges. It becomes difficult for hundreds of horizontal ranges, because of the growing inline cells to add to the SUM Functions. It is also prone to errors. And possibly it can break if rows or columns are added afterwards.

Trials:

I originally didn't think of using the INDIRECT Function (I never needed before). But I don't know any other Google Sheets function able to achieve the end results in a simpler way.

Questions:

What way to avoid the SUM Function method for the same result would you suggest, for a formula solution?

For a formula, what simpler function-s than the INDIRECT and/or SUM would be more efficient?

I also thought of using a script for doing that, but I can't put the whole idea into a manageable script concept process. What would you suggest if a script would be more appropriate?

Many thanks for your help!

The Sample Sheet:

Sample Sheet

EDIT:

I just found about the ADDRESS Function from this answer by Player0 (to help greatly simplify the INDIRECT function row and column references):

Google Sheets: INDIRECT() with a Range

ADDRESS Function Example

References:

Excel INDIRECT Function

Google Sheets ADDRESS Function

Lod
  • 657
  • 1
  • 9
  • 30
  • Hi NaziA and thanks a lot for the request! Yes, the count should be per value, returning each value's ordinal followed from the 1st range into each following ranges. The purpose is to keep track at a glance what ordinal number is the current value regardless of the ranges looked at. I was thinking it might be possible for a script to specify a general range to make the count apply to. For example in the Expected result the range from J21 to N31 (J31:N31), or until whatever the last row is (J21:N). Do you think that would work with your script? Thank again! – Lod Dec 30 '21 at 18:18

1 Answers1

1

I was able to create a script to show the ordinal number of the replicates but is only respective to one range. EDIT: I have modified it to also accept multiple row ranges. See updated answer below:

Script:

function showOrdinal(range) {
  range = "A4:E12";
  var values = SpreadsheetApp.getActiveSheet().getRange(range).getValues();
  var output = [];
  var order, subTotal;
  values.forEach((x, i) => {
    if(x.flat().filter(String).length) {
      subTotal = values.slice(0, i + 1).flat().filter(String);
      order = x.filter(String);
      if (order[0] != '-') {
        var row = order.map(x => {
          return getNumberWithOrdinal(subTotal.filter(e => e == x).length);
        })
        row = [...row, ...Array(x.length - row.length)];
        output.push(row);
        if(output.length > 2)
          output.splice(output.length - 2, 1);
      }
      order = [...order, ...Array(x.length - order.length)];
      output.push(order)
    }
    else
      output.push(x);
  });

  // console.log(output);

  SpreadsheetApp.getActiveSheet().getRange(3, 21, output.length, output[0].length).setValues(output);
  // return output;


}

// get ordinal number
// https://stackoverflow.com/a/31615643/14606045
function getNumberWithOrdinal(n) {
  var s = ["th", "st", "nd", "rd"],
      v = n % 100;
  return n + (s[(v - 20) % 10] || s[v] || s[0]);
}

Output:

output

Note:

  • The range that is to be passed assumes that the first row should be the first range, not a blank one. And also the last row of the range should contain the last row of the data.
  • Any unrelated data should be starting with - on the first column so it can be allowed without processing it.
NightEye
  • 10,634
  • 2
  • 5
  • 24
  • Oh you just beat me with the answer :) ! I'll study it now! – Lod Dec 30 '21 at 18:19
  • 1
    This was only applicable for a single range that has been passed. I did not try to process/test multiple rows as I haven't yet fully understood the data and goal here @Lod. But this is a place to start. If you have questions about the script above, feel free to ask. – NightEye Dec 30 '21 at 18:22
  • Ok, Thanks a lot for the script and and example. I think we could use it with a small modification. In the Illustration Example, I set in I22, I26 and I30, respectively, 4, 5 and 3. In my end use I would have those values predefined. Would it be possible to modify your script to: 1. Grab those dynamic values 4,5,6 (from a predefined range, say T1:T3) and 2. Use them as references to slice either the Horizontal contiguous range or the Vertical one (for ex. as start and end coordinates of the smaller ranges), and 3. Get the sliced range parts and set them in new non contiguous ranges. – Lod Dec 30 '21 at 18:40