1

I need to convert some data in my Google sheet. Attached is the screenshot on how I currently have the data and how I am looking to format the data into.

enter image description here

pgSystemTester
  • 8,979
  • 2
  • 23
  • 49

4 Answers4

1

assuming your data starts in cell Sheet1!A2 and extends indefinitely down and indefinitely over, try this:

=ARRAYFORMULA(QUERY(SPLIT(FLATTEN(Sheet1!A3:A&" "&Sheet1!B2:2&"|"&OFFSET(Sheet1!B3,,,9^9,9^9)),"|",0,0),"where Col2 is not null",0))
MattKing
  • 7,373
  • 8
  • 13
  • This ignited some *great* ideas, but it doesn't really what the question was asking for work. You might want to pull it down. – pgSystemTester Aug 13 '21 at 15:14
  • @pgSystemTester I don't really understand. I think my answer provides the exact solution that was being looked for? I will update it to have indefinite ranges if you think that's helpful. – MattKing Aug 13 '21 at 15:34
  • I did notice that I didn't properly test your first solution as I didn't line up the data properly with `A2` vs `A1` (your presumption makes sense based on the original graphic -- my fault). However that produced three columns. I can't get your revised solution to work. Checkout the shared file I posted and feel free to edit or add a tab. I'm curious about your offset approach. https://docs.google.com/spreadsheets/d/1pP7lVekF5fcjEpzUkD_OqmBAVMP4DNvyAH2fX7gFE1g/edit?usp=sharing – pgSystemTester Aug 13 '21 at 16:46
  • I seem to have missed a closing parenthesis in my example formula. I fixed it in your sheet and edited my solution above. – MattKing Aug 16 '21 at 01:51
  • Confirmed! Nice. I upvoted. I'm glad I checked, thanks for clarifying. – pgSystemTester Aug 16 '21 at 22:40
1

Derived from MattKing's answer, I've added transpose in conjunction with some manipulation on the concatenation part to follow the type of sorting your Required Data Format had. This should give you the same output you provided above.

Sample Data:

sample

Formula:

=arrayformula(split(flatten(transpose(A2:A6&" "&B1:D1)&"|"&transpose(B2:D6)),"|"))

Where:

  • A2:A6 is the range of your project names
  • B1:D1 is the range of your headers
  • B2:D6 is the range of your dates

Result:

result

EDIT:

If you are expecting blank cells in your dates such as the sample below (as pgSystemTester mentioned in the comments section):

sample

You need to add query and exclude those rows that doesn't have dates.

=arrayformula(query({split(flatten(transpose(A2:A6&" "&B1:D1)&"|"&transpose(B2:D6)),"|")}, "where Col2 is not null"))

output

NightEye
  • 10,634
  • 2
  • 5
  • 24
  • 1
    That's cool. I was looking for something like that. Can you account for a dynamic range that excludes blank cells? That's the only advantage my solution might have over yours. Thanks for posting. – pgSystemTester Aug 12 '21 at 20:24
  • Hi @pgSystemTester, I could definitely add a query on that same as what MattKing did provide. Let me test it and update my answer. – NightEye Aug 12 '21 at 20:25
  • @pgSystemTester, adding query works. see updated answer above. – NightEye Aug 12 '21 at 20:28
  • I'll have to think of a way for providing an answer that expects a dynamic range. – NightEye Aug 12 '21 at 20:32
  • 1
    I think your example for missing milestones would be to the right, not left but not a big deal. Still a solid answer... any time custom functions can be avoided, that's a good think. Upvoting! – pgSystemTester Aug 12 '21 at 20:35
  • Thanks @pgSystemTester, regarding the dynamic range, I think it would be hard to provide given the formula above, it might need a different approach to do it. But definitely, it would be much easier if OP would consider the custom formula you provided if it comes to that. – NightEye Aug 12 '21 at 20:38
  • Also, my question was for blank headers, not blank dates. Our answers perform the same with blank dates, although I could easily adjust. Like I said the only advantage I might see over yours is that OP could put a custom formula of like `=buildTwoColumnsData(a:z)` and just leave it like that as data came in or got deleted. – pgSystemTester Aug 12 '21 at 20:41
  • Here is the de facto answer solution that we were looking for. See my answer for where I stole it from: `=INDEX(QUERY(SPLIT(FLATTEN(IF(B2:E="",,A2:A&" "&B1:E1&"×"&B2:E)), "×"), "where Col2 is not null"))` – pgSystemTester Aug 13 '21 at 15:10
  • OFFSET is the best way to have a 2d dynamic range (imo) you use the vertex of the range as an anchor, then 9^9 for the height and width: =OFFSET(B2,,,9^9,9^9) – MattKing Aug 13 '21 at 15:32
  • @MattKing I hate offset, but I can tell you're no slouch, so I'm curious how that could work. Mind posting on this sheet to illustrate? I think Player() has conquered all of us with the best answer, but I am still getting used to arrays/spill stuff. https://docs.google.com/spreadsheets/d/1pP7lVekF5fcjEpzUkD_OqmBAVMP4DNvyAH2fX7gFE1g/edit?usp=sharing – pgSystemTester Aug 13 '21 at 16:49
1

Just to give another option, and spurred on by this (likely repeat) question

=LET(x, $I$2:$L$4,
myrows, ROWS(x),
mycols, COLUMNS(x),
mycount, SEQUENCE(myrows*mycols),
car, $H$2:$H$4, color, $I$1:$L$1,
mylist, car&" "&color,
mycolumn, INDEX(mylist, CEILING(mycount/mycols,1), IF(MOD(mycount,mycols)=0, mycols,MOD(mycount,mycols))),
mydata, INDEX(x, CEILING(mycount/mycols,1), IF(MOD(mycount,mycols)=0, mycols,MOD(mycount,mycols))),
IF(SEQUENCE(1,2)=1, mycolumn, mydata))

enter image description here

EDS
  • 2,155
  • 1
  • 6
  • 21
  • 1
    Yep. But sheets will have it soon I am guessing. So weird these two questions came out so close together... these peeps are probably in some class together and we're doing their homework! :) – pgSystemTester Aug 13 '21 at 15:13
0

You can see a sample sheet here, with several of answers illustrated on each tab.

Formula With No App Scripts

This is an update and includes a formula that was stolen from a different answer that appears to nail it. (Player() is officially the Jack Bauer of Spreadsheets... don't ask how he gets things done!).

=INDEX(QUERY(SPLIT(FLATTEN(IF(B2:E="",,A2:A&" "&B1:E1&"×"&B2:E)), "×"), 
 "where Col2 is not null"))

Make sure this is placed off to the right so that it doesn't spill into itself.

My Original App Scripts Solution

This is honestly not needed any more, but I worked on it for half an hour and maybe someone might find it helpful if they have trouble sleeping.

/**
 * Creates 2 Columns of Data
 *
 * @param {range} theRangeValues The Table Range.
 * @return The two columns of data with all combinations based on rows and columns
 * @customfunction
 */
function buildTwoColumnsData(theRangeValues) {
  var allValues = [];
  var i = 1;
  var cCell = theRangeValues[0][i];
  while (cCell != '' && i < theRangeValues[0].length) {
    var theEnd = cCell;
    var g = 1;
    var rCell = theRangeValues[g][0];
    while (rCell != '' && g < theRangeValues.length) {
      allValues.push([rCell + " " + theEnd, theRangeValues[g][i]])
      g++;
      if (g < theRangeValues.length) {
        rCell = theRangeValues[g][0];
      }
    }
    i++;
    if (i < theRangeValues[0].length) {
      cCell = theRangeValues[0][i];
    }
  }
  return allValues;
}
pgSystemTester
  • 8,979
  • 2
  • 23
  • 49