3

I'm writing a spreadsheet to keep track of a small business' financials. They operate a few Rooms for rent, and the structure of the document is made so that each sheet holds a year's worth of booking for all the rooms.

Essentially, each row is defines a specific date, while each rooms spans a few columns (reason is that they don't just want to track whether or not a room is booked, but also record names of clients & other remarks), among which the daily calculated income (some factors alter the daily rate each room will generate).

Example

So this is all fine and dandy, and I've created named ranges for each month of the year, and for each room.

For example, rows 6:36 will represent the month of January, while columns C:I will represent Room 1. Room 2 will span J:P and so forth.

Now, in another sheet, I wanted to make a dashboard which lists the earning for each room, per month. It's a very simple table with 12 rows (one for each month) and 10 columns (1 for each room) where I planned to sum up all the earnings.

Dashboard

So my issue is that I can't find a way to retrieve a specific column of a named range for a room ('vertical named range'), which is also limited in a named range for a month ('horizontal named range'). I had read about using ARRAYFORMULA(INDEX(named_range, ,wished_column)) but that only works for a single named range. My knowledge of these two functions being non-existent, I didn't manage to extend it to a 2-named-range version...

(I mean I did try something along the lines of ARRAYFORMULA(INDEX(January, , INDEX(Room1, , 3))) but that didn't work)

So because there isn't a one-to-one relation from the Dashboard cells to the Rooms cells, my current only solution is to manually reference everything, which you'll understand is inefficient and time-consuming...

My question, in fine, is: How can I retrieve a range that results of the intersection of 2 (or more) named ranges ? Once I have that resulting range, I know it will be very easy to use INDEX().

Skwiggs
  • 1,348
  • 2
  • 17
  • 42

2 Answers2

6

Define a named range Base as

A:Z

Define a range named Horizontal as

6:36

Define a range named Vertical as

C:I

Then the intersection of the vertical and horizontal ranges is given by:

index(Base,row(Horizontal),COLUMN(Vertical)):index(Base,row(Horizontal)+rows(Horizontal)-1,COLUMN(Vertical)+columns(Vertical)-1)

This can be verified by using it in a function e.g.

=countblank(index(Base,row(Horizontal),COLUMN(Vertical)):index(Base,row(Horizontal)+rows(Horizontal)-1,COLUMN(Vertical)+columns(Vertical)-1))

gives the result 7 * 31 = 217 in my sheet because I haven't filled in any of the cells.

The Offset version of this would be:

=countblank(offset(A1,row(Horizontal)-1,COLUMN(Vertical)-1):offset(A1,row(Horizontal)+rows(Horizontal)-2,COLUMN(Vertical)+columns(Vertical)-2))

or more simply:

=countblank(offset(A1,row(Horizontal)-1,COLUMN(Vertical)-1,rows(Horizontal),COLUMNS(Vertical)))

So this works well in OP's case where you have two fully overlapping ranges like this:

enter image description here


Partial Overlap

Suppose you have two partially overlapping ranges like this:

enter image description here

You can use a variation on the standard overlap formula (This is one of the early references to it as used with a date range)

max(start1,start2) to min(end1,end2)

So the previous formula becomes

=countblank(index(Base,max(row(index(Partial1,1,1)),row(index(Partial2,1,1))),max(COLUMN(index(Partial1,1,1)),column(index(Partial2,1,1)))):
index(Base,min(row(index(Partial1,1,1))+rows(Partial1)-1,row(index(Partial2,1,1))+rows(Partial2)-1),min(COLUMN(index(Partial1,1,1))+columns(Partial1)-1,column(index(Partial2,1,1))+columns(Partial2)-1)))

and the offset version is

=countblank(offset(A1,max(row(offset(Partial1,0,0)),row(offset(Partial2,0,0)))-1,max(COLUMN(offset(Partial1,0,0)),column(offset(Partial2,0,0)))-1):
offset(A1,min(row(offset(Partial1,0,0))+rows(Partial1)-2,row(offset(Partial2,0,0))+rows(Partial2)-2),min(COLUMN(offset(Partial1,0,0))+columns(Partial1)-2,column(offset(Partial2,0,0))+columns(Partial2)-2)))

I have tested this on ranges C2:F10 and D3:G11 which gives the result 24 as expected.

However, if there is no overlap, this can still give a non-zero result, so a suitable test needs adding to the formula:

=if(and(max(row(index(Partial1,1,1)),row(index(Partial2,1,1)))<=min(row(index(Partial1,1,1))+rows(Partial1)-1,row(index(Partial2,1,1))+rows(Partial2)-1),
max(column(index(Partial1,1,1)),column(index(Partial2,1,1)))<=min(column(index(Partial1,1,1))+columns(Partial1)-1,column(index(Partial2,1,1))+columns(Partial2)-1)),"Overlap","No overlap")

Perhaps the best approach in Google Sheets is to go back to the full version of the Offset call OFFSET(cell_reference, offset_rows, offset_columns, [height], [width]) . Although this is rather long, it will return a #Value! error if there is no overlap:

=Countblank(offset(A1,
max(row(offset(Partial1,0,0)),row(offset(Partial2,0,0)))-1,
max(COLUMN(offset(Partial1,0,0)),column(offset(Partial2,0,0)))-1,
min(row(offset(Partial1,0,0))+rows(Partial1),row(offset(Partial2,0,0))+rows(Partial2))-max(row(offset(Partial1,0,0)),row(offset(Partial2,0,0))),
min(COLUMN(offset(Partial1,0,0))+columns(Partial1),column(offset(Partial2,0,0))+columns(Partial2))-max(COLUMN(offset(Partial1,0,0)),column(offset(Partial2,0,0)))
))

Notes

Why did I have to introduce some more indexes (indices?) in the second formula to make it work? Because if you use the row function with a range in an array context, you get an array of row numbers which isn't what I want. As it happens, in the first formula you are not using it in an array context, so you just get the first row and column of the given range which is fine. In the second formula, Max and Min try to evaluate all the rows in the array, which gives the wrong answer, so I have used Index(range,1,1) to force it to look only at the top left hand corner of each range. The other thing is that both index and offset return a reference, so it is valid to use the construct Index(...):Index(...) or Offset(...):Offset(...) to define a new range.

I have also tested the above in Excel (where as mentioned the Index version would be preferable). In this case Base would be set to $1:$1048576.

Although in Excel you have the Intersect Operator (single space) so it's not necessary to use an Index or Offset formula at all e.g. the first example above would simply be:

=COUNTBLANK(Vertical Horizontal)

and if there is no overlap the formula returns a #NULL! error.

Tom Sharpe
  • 30,727
  • 5
  • 24
  • 37
  • Thanks a lot for the answers, this looks exactly like what I've been trying to achieve ! I have yet to give it a try :) However I'm curious as to why you're using a Base named range. Is it just to specify the entire area of used cells you want to take into account ? – Skwiggs Oct 12 '18 at 13:33
  • Yes, exactly. Index can only be used to pinpoint a cell, row or column within an existing range. I could have used Offset though..I guess I'm thinking partly with my Excel hat on, where the use of offset is avoided where possible. – Tom Sharpe Oct 12 '18 at 13:45
  • Have added an offset version of the first formula to my answer. – Tom Sharpe Oct 12 '18 at 15:56
  • This solution works perfectly for me, thanks a lot for the hard work ! Have selected your answer as accepted and delivered the Bounty :) – Skwiggs Oct 15 '18 at 08:59
  • 1
    @TomSharpe Congratulations. A remarkable effort of logic and skill. It seems churlish to mention that there's a typo in the equation that follows "So the previous formula becomes". The formula should read: `=countblank(index(Base,max(row(index(Partial1,1,1)),row(index(Partial2,1,1))),max(COLUMN(index(Partial1,1,1)),column(index(Partial2,1,1)))):index(Base,min(row(index(Partial1,1,1))+rows(Partial1)-1,row(index(Partial2,1,1))+rows(Partial2)-1),min(COLUMN(index(Partial1,1,1))+columns(Partial1)-1,column(index(Partial2,1,1))+columns(Partial2)-1)))` – Tedinoz Oct 16 '18 at 03:39
  • You are too kind! Well spotted - I somehow missed a whole section of the formula when copying it across - hopefully it is OK now. – Tom Sharpe Oct 16 '18 at 08:26
  • This is great, thanks! Any sense of the performance implications of your technique? When constructing formulas (and conditional formulas) I'd prefer, for readability, to reference cells by the name of their column. In the past I've used this approach in Excel but it got unbearably slow when the number of rows and columns in my sheet scaled up (even after a great deal of tuning). – rkagerer Mar 16 '23 at 22:47
  • I don't have any timings for the formulas, but I would imagine that their performance wouldn't be too bad because there isn't any iteration involved. You could certainly shorten them now that Let is available. I didn't quite get your second point about referencing cells by the name of their column, I'm afraid with advancing years my brain is a little slow nowadays. – Tom Sharpe Mar 18 '23 at 09:14
2

"I've created named ranges for each month of the year, and for each room. For example, rows 6:36 will represent the month of January, while columns C:I will represent Room 1. Room 2 will span J:P and so forth."

What I suggest is that if "January" is defined for columns C to whatever (the last column of the last room), then that's all you need.

You haven't shown us the layout of the dashboard. But let's assume that at the very least you're interested in the income generated by each room.

=query({January},"select sum(Col3)   label sum(Col3)'' ")

In this image, the range called "January" is highlighted. Note that it does NOT include the header. Note also that it can be many columns wide; in this example, I've just made up a few columns, but your range should cover all the columns for rooms 1 to n.

screenshot

Syntax: QUERY(data, query, [headers])

Data: This formula queries the range called "January". That range can be on the same sheet, on on another sheet (such as your Dashboard). Reminder: in this screenshot, "my version of "January" is highlighted.

Query to count Number of People: "select sum(Col3) label sum(Col3)'' "

Query to sum the income earned: "select count(Col2) label count(Col2)'' "

Col2 & Col4 = Number of People for Room#1 and Room#2 respectively.

Col3 & Col5 = Income for Room#1 and Room#2 respectively.
[headers]: You can ignore them.

This formula delivers just the value of the query; even though it includes a "label", the label will not print.

Modify and adapt these formulae to create the other information required for your Dashboard.

Tedinoz
  • 5,911
  • 3
  • 25
  • 35
  • Thanks for the answer. I did notice however you didn't assign named ranges for each room (columns). Your current solution works but is not applicable for Autofill if I were to click & drag the formula to neighbouring cells (in that the references would only be incremented by 1 while one room currently spans 7 columns). I have updated my question with more illustrations of what I have and what I'm trying to do. – Skwiggs Oct 08 '18 at 08:31
  • Point taken. There are a couple of options about how to build the formulae. Worst case, I suppose is that editing 10 formula wouldn’t take more than 5 minutes. – Tedinoz Oct 08 '18 at 08:52
  • But so, there isn't any simpler way to get a range intersecting two named ranges for instance ? Then using INDEX() would be super easy – Skwiggs Oct 08 '18 at 09:43
  • This is something that you are going to do ONCE in the life of this spreadsheet. Presumably you researched widely looking for examples of intersections of vertical and horizontal named ranges and couldn't find any existing solutions. So this question is something of a last resort? But in the time that we have spent discussing this, you could have copied and edited the formula and your spreadsheet would be up and running. There is a saying that "Perfect is the enemy of good". Hold out for the "perfect" solution if you wish but, in the meantime, reflect on what your research has told you. – Tedinoz Oct 08 '18 at 10:04
  • No your assumption that I'm going to do this once is wrong; I will be doing this for all sites of the client and will also add other statistics tracking to the dashboard later. Besides my question clearly mentions `named range within named range` so while I appreciate your help so far your current answer is not what I was looking to do (or doesn't explicitly prove that what I'm trying to achieve is impossible given the tool). – Skwiggs Oct 08 '18 at 10:57