Excel, append one range to the end of another in one column - excel

I have two columns of data in Excel. I would like to add a third column which combines the first and second. How can I do this with a formula such that I can add or remove data from columns A and B without ever having to touch column C?
Column A Column B Column C
Bob Mary Bob
Joe Melissa Joe
Jim Jackie Jim
Mary
Melissa
Jackie

The question explicit mention Microsoft Office Excel but I think would be good to add that if you are using Google Sheets a simpler solution is to use the curly brackets function/operator as mentioned by Lake at https://stackoverflow.com/a/14151000/1802726.

Here is a simple solution using FILTERXML and TEXTJOIN that can append MULTIPLE RANGES OF ANY SIZE, ARRAY FORMULAS AND REGULAR FORMULAS. Just replace YOUR_RANGES with the ranges or dynamic arrays you wish to join:
Simple version that ignores empty cells:
=FILTERXML("<A><B>" & TEXTJOIN("</B><B>",TRUE,YOUR_RANGES) & "</B></A>", "//B")
This one includes empty cells:
=IFERROR(FILTERXML("<A><B>" & TEXTJOIN("</B><B>",FALSE,YOUR_RANGE) & "</B></A>", "//B"), "")
If your input data contains the "<" character, the formulas above will return an error, so use this one instead:
=IFERROR(SUBSTITUTE(FILTERXML("<A><B>" & SUBSTITUTE(SUBSTITUTE(TEXTJOIN("ΨΨ",FALSE,YOUR_RANGE),"<","ЉЉ"),"ΨΨ","</B><B>")&"</B></A>","//B"),"ЉЉ","<"),"")
Note: you can change the FALSE to TRUE to ignore empty cells.
Note 2: You can replace the characters ЉЉ and ΨΨ by any character(s). I used these specific characters because it is very unlikely that your input data will contain ЉЉ or ΨΨ, which would cause errors.
NOTES:
Tested on:
Excel 365
EXAMPLE:
Using the simple version of the formula:
=FILTERXML("<A><B>" & TEXTJOIN("</B><B>",TRUE,A1:A3,B1:B3,C1:C3) & "</B></A>", "//B")
As a result you will get a dynamic array with the joined/appended ranges:
You can then apply any dynamic array formula (like UNIQUE) to the result.
HOW THIS WORKS:
The JOINTEXT function grabs your ranges and joins them as a text with the delimiter "</ B >< B >". Then, after adding "< A >< B >" to the beginning and "</ B ></ A >" to the end, we have an XML formatted text:
<A><B>1</B><B>2</B><B>3</B><B>A</B><B>B</B><B>C</B><B>!</B><B>#</B><B>#</B></A>
Finally, the FILTERXML will separate the tags into a dynamic array which will be the joined/appended ranges.

Enter the following formula into cell C1
=IF(ROW()>COUNTA(A:B),"",IF(ROW()<=COUNTA(A:A),INDEX(A:A,ROW()),INDEX(B:B,ROW()-COUNTA(B:B))))
Then copy down as far as you need.

Here's a nice way of interleaving the two rows.
In other words, turning this:
A X
B Y
C Z
into this:
X
A
Y
B
Z
C
Say the above table is in columns one and two, you'd do:
=IF(MOD(ROW(),2)=0,INDIRECT(ADDRESS(INT(ROW()/2), 1)),
INDIRECT(ADDRESS(INT(ROW()/2)+1, 2)))
Explanation
Let's break that down a little. The first part is MOD(ROW(), 2) which returns a zero if the current row is even, and a one if it's odd.
So the IF goes FALSE/TRUE/FALSE/TRUE as we go down the column.
Next, the ADDRESS(INT(ROW()/2), 1) returns us a string representation of the address of the cell at column 1 and at half the current row. (Rounded down). This piece on its own looks like:
#VALUE!
$A$1
$A$1
$A$2
$A$2
$A$3
$A$3
(That first #VALUE error is because 1/2 = 0.5 which rounds down to zero. There's no row zero!)
The INDIRECT function returns whatever value is found at that address.
The rest is pretty clear.
NOTE: There may be a slicker way than using INDIRECT and ADDRESS. Suggestions welcome.

Related

Need to count contents of cells to produce a knitting pattern

So this needs a bit of detail:
n,X,X,X,n is in cells B5 to F5
I need to get the following output:
1n,3x,1n
for this particular row.
Now the n's and X's represent stitches in knitting with the "n" being the background color and the "x" being the front color.
There is an array of cells B5:F12 representing the rows and stitches, so each row will have a different arrangement of stitches or background color.
I need to avoid vba as this needs to be as stable as possible with the user being my Mum who is 90 years old :) and all she needs is a place to enter the name and the layout (which I have done) and a pattern list for each row (also sorted).
I have started to consider things like:
if(B5=C5,1&B5,"")
But given the n umber of combinations that becomes very long.
Any ideas? Cheers.
You could try:
Formula in H5:
=BYROW(B5:F12,LAMBDA(x,LET(z,REDUCE(VSTACK(TAKE(x,,1),1),DROP(x,,1),LAMBDA(a,b,IF(b=#TAKE(a,,-1),IF(COLUMNS(a)=1,VSTACK(b,TAKE(a,-1)+1),HSTACK(DROP(a,,-1),VSTACK(b,DROP(TAKE(a,,-1),1)+1))),HSTACK(a,VSTACK(b,1))))),TEXTJOIN(",",,DROP(z,1)&TAKE(z,1)))))
I'll see if I can golf the bytecount down a bit...
EDIT:
After a considerable amount of golfing (came down to 119), I came up with:
=BYROW(B5:F12,LAMBDA(x,MID(REDUCE("",x,LAMBDA(a,b,IF(RIGHT(a)=b,LEFT(a,LEN(a)-2)&1+LEFT(RIGHT(a,2)),a&",1")&b)),2,99)))
Though less dynamic than the 1st one, but possible due to the fact there are only <10 columns for each knitting pattern.
If your mother doesn't have the latest Excel (with LAMBDA etc), here is an alternative to #JvdV's answer which only uses LET,SEQUENCE and FILTER.
It only accepts a single row, so you'd need to fill the formula down.
=LET(p,LOWER(B5:F5),c,COLUMNS(p),s,SEQUENCE(,c),
a,IF(s=c,c,IF(INDEX(p,,s)<>INDEX(p,s+1),s,0)),
b,FILTER(a,a>0),t,SEQUENCE(,COLUMNS(b)),
n,IF(t=1,INDEX(b,,t),INDEX(b,,t)-INDEX(b,,t-1)),
TEXTJOIN(",",TRUE,n & INDEX(p,,b)))
I might add that it allows for adding more than one colour into the pattern ...
and with a bit of conditional formatting, the good lady can design her own multicolour patterns!
This is just a start of a solution, but in cell "B6" you can put the formula:
=(IF(B5=A5,A6+1,1))
This will generate following list:
B C D E F
5: n x x x n
6: 1 1 2 3 1
From there, you can try to get the Subtotals feature to work, based on the Max formula, ... (as I said, this is just a start).
If you are willing to spread the logic over multiple sheets, it's quite easy to come up with a way to do this. Consider a workbook with three sheets:
Pattern
EqualPrevCol, where each cell of Pattern is checked for equality against the previous column of the same row.
The formula for cell EqualPrevCol!D3 is:
=Pattern!D3=Pattern!C3
And finally PatternResult, where most of the logic resides:
Consider one row of EqualPrevCol:
At every FALSE column, we want to know how many columns until the next FALSE. To do this, we want to find the next exact MATCH for D3 in the rest of the row:
=MATCH(EqualPrevCol!D3, EqualPrevCol!E3:$H3, 0)
If no match is found, that means the rest of the row is all TRUE. In this situation, we want to return the length of the rest of the row plus this current cell.
=IFNA(MATCH(...), COLUMNS(D3:$H3))
And finally, we append this to the current character:
=IFNA(...) & Pattern!D3
Also, if the 7 row at this column is TRUE, we want to keep this blank:
=IF(EqualPrevCol!D3, "", IFNA(...) & ...)
The full formula of cell PatternResult!D3 is:
=IF(EqualPrevCol!D3, "", IFNA(MATCH(EqualPrevCol!D3, EqualPrevCol!E3:$H3, 0), COLUMNS(D3:$H3)) & Pattern!D3)
Finally, the pattern is condensed to the Pattern sheet. The Pattern!B3 cell contains:
=TEXTJOIN(", ", TRUE, PatternResult!D3:$H3)
To scale this up, you simply need to change all occurrences of $H in the formulas (this was a reference to the last column) and re-fill the cells on the latter two sheets.

Mode Text 2nd most common text value

IFERROR(INDEX($I$7:$I,MODE(IF($I$7:$I<>"",MATCH($I$7:$I,$I$7:$I,0)))),"No data")
With this formula, which calculates the most common text value, I need to have the 2nd most common.
Column I content:
Apple
Orange
Apple
Apple
Orange
In this example, I need to get Orange. How is that possible? I can't figure how.
A PivotTable might suit:
and copes with ties for rank.
You can extract the most frequent item in the list with an array formula.
=INDEX(MyList,MATCH(MAX(COUNTIF(MyList,MyList)),COUNTIF(MyList,MyList),0))
Note that an array formula must be confirmed with Shift+Ctl+Enter instead of the customary singular Enter required for normal formulas. When entered wrongly it will display a #NUM! error.
For simplicity's sake I have used a named range MyList in the formula. However, if you prefer, you can replace the name with something like $I$7:$I$1000.
To extract the second-most frequent expression in the list you could use a formula constructed analogue to the above.
=INDEX(MyList,MATCH(LARGE(COUNTIF(MyList,MyList),MAX(COUNTIF(MyList,MyList))+1),COUNTIF(MyList,MyList),0))
This formula is built on the logic that n equals the highest number of occurrences. Therefore the second highest must rank as n + 1, being MAX(COUNTIF(MyList,MyList))+1) in the above formula. By the same method the third ranked could be extracted.
You can embed these formulas in an IFERROR() function.
I found this on Mr Excel
Return most common, 2nd most common, 3rd most common, etc. text string in an array
Spreadsheet Formulas
Cell ___ Formula 'Notice that the cells are B2, D2, E2. Column C is blank
B2 =IF(A2="","",IF(COUNTIF(A$2:A2,A2)=COUNTIF($A$2:$A$100,A2),COUNTIF($A$2:$A$100,A2)+(ROW()/1000),""))
D2 =IF(ROWS($1:1)>COUNT(B:B),"",INDEX(A:A,MATCH(LARGE(B:B,ROWS($1:1)),B:B,0)))
E2 =IF(D2="","",COUNTIF($A$2:$A$100,D2))<br><br>
Results
___ A ________ B ___C ___D _________E
1 Data Set:___Helper ____ Name ____ Occurrences
2 Harmon _____________ Williams ______4
3 Smith _______________ Smith ________3
4 Smith _______________ Harmon ______2
5 Harmon_____ 2.005
6 Williams
7 Williams
8 Smith _______3.008
9 Williams
10 Williams ____4.010
you can try to tie this all together in a single formula but it's simpler and more agile in a spreadsheet environment to just break out the problem in a few separate steps.
take a given column of values you're wanting to count/rank - i'll call it RankList in examples below.
if you're not setting named ranges (do yourself a favor and use named ranges) you'll want this to be your column range - i.e. A:A
now in another column use
=unique(RankList)
there's your list of unique values, now we just need to count the instances of each unique value in the original RankList - this is simple - in the next column over simply use
=countif(RankList,B1)
B1 above represents the cell adjacent to the formula, wherever that might be on your sheet. now autofill the formula in with the relative cell value for each item. now all of your items are counted by instance.
now we want to sort them by value, highest to lowest. create another named range, selecting the two columns containing the =unique(RankList) and =countif(RankList,B1) formulas that were just created, i'll refer to it as UniqueCount
use the following
=sort(UniqueCount, 2, false)
that's it. again you can accomplish this by stacking formulas like in the above examples, but in practice i've found that you won't know what you'll want to do additionally with your data/sheet later on. keeping it broken up in discrete steps like this makes it much easier to make adjustments.

Looking up a value in a specified column

Something I've wanted to do quite a bit lately, and can't work out how to do, is MATCH in a column I pass as an argument. Essentially, I have a two dimensional array, and I want to be able to find the first occurrence of a given value in the nth column, for any given value of n, and return the row number it occurs at. Alternatively (and more-or-less equivalently), I want to be able to search in the column with a given column header. Is there any way to do this?
Effectively, I want to simulate the non-existent function =MATCH(lookup_value,lookup_array,lookup_column,[match_type])
I've kludged together a horrible bodge job using INDIRECT, which works, but offends me horribly.
=MATCH(lookup_value,INDIRECT("R"&<top of array>&"C"&<left of array>+<column reference>&":R"&<bottom of array>&"C"&<left of array>+<column reference>,FALSE),FALSE)
This formula should work for you and will avoid INDIRECT. Anytime you can avoid using Indirect, I recommend doing so.
=MATCH(lookup_value,INDEX(lookup_array,0,MATCH(lookup_header,array_headers,0)),0)
If you aren't looking up the column by column header and just have the column number, then it becomes easier:
=MATCH(lookup_value,INDEX(lookup_array,0,column_number),0)
You could do something like this:
Set findCell = ActiveSheet.Range("A:Z").Find(What:="term_to_search")
Will select a header based on your search term.
Set range = ActiveSheet.Range(findCell, findCell.Offset(DEF_MAX_ROWS, 0))
Set up a range which will search from that header down a whole column.
For column references beyond Z you might switch notation (Excel Options, Formulas, Working with formulas and check R1C1 reference style) and, assuming the value to be looked up is in 'A1' (R1C1) with the column number in 'A2' (R2C1) apply:
=MATCH(R1C1,INDIRECT("C"&R2C1,0),0)
to save some complexity in converting a string of two or three characters into the relevant column number.
Say we have a two dimensional array: B3:E17 and we wish to locate Happiness in the third column of that array.In G1 enter:
B3:E17
In G2 enter:
3
In G3 enter:
=ADDRESS(ROW(INDIRECT(G1)),COLUMN(INDIRECT(G1))+$G$2-1) & ":" & ADDRESS(ROW(INDIRECT(G1))+ROWS(INDIRECT(G1))-1,COLUMN(INDIRECT(G1))+$G$2-1)
This will display the address of that third column. Then in G4 enter:
=MATCH("Happiness",INDIRECT(G3),0)
For example:
You can specify a range in a formula using the INDIRECT function. So, for example, if you put the letter designation for the column you want to search in cell A75, you could use:
=MATCH("Value_To_Match", INDIRECT(A75 & ":" & A75), 0)
So, if the value in A75 is G, the string built up in the INDIRECT call is G:G, and the MATCH will look through column G for "Value_To_Match" and return the row number in which it's found.
Using this idea, you can put a formula into A75 that generates the column designation for the column you want to search. For example, if your column headers are all in row 1, and the header you want to search for is in A74, you can do:
=CHAR(MATCH(A74, 1:1, 0) + 64)
using the CHAR function to convert numbers into ASCII characters, so 65 becomes A, 66 becomes B, etc. Note that this will only work if you don't have columns past Z. You'd need a more fussy formula to do the right thing with AA, etc.
You can overcome the annoyances of dealing with column letters by using R1C1 notation instead, which you can activate by adding a second parameter of FALSE to the INDIRECT expression. Now, instead of specifying your column by a letter, you'll specify it using a number. This simplifies the column-finder in A75:
=MATCH(A74, 1:1, 0)
and also the INDIRECT expression in your overall MATCH:
=MATCH("Value_To_Match", INDIRECT("C" & A75, FALSE), 0)

Formula to find match in two-dimensional range

I need a formula that will look up a value in a 2-dimensional range and return the coordinates or cell address of the matching cell. For example:
R A B C
1 John Matt Pete
2 Sara Bret Chad
3 Lila Maya Cami
I want to search the range A1:C3 for Chad and return C2 or 2,3. How can I accomplish this using Excel formulas? (I'll actually end up applying this to Google Sheets).
Thanks!
Old question, but I thought I'd share a much simpler and elegant answer here that doesn't involve helper columns or complicated formulas, so that more people will get things done easier. Assuming that the table contains unique values and that you use E1 to store your search string Chad and E2 to display the result:
if you want the row and column result of 2,3 in E2:
=SUMPRODUCT((A1:C3=E1)*ROW(A1:C3)) & "," & SUMPRODUCT((A1:C3=E1)*COLUMN(A1:C3))
if you want the R1C1 style cell address string of C2 in E2:
=ADDRESS(SUMPRODUCT((A1:C3=E1)*ROW(A1:C3)),SUMPRODUCT((A1:C3=E1)*COLUMN(A1:C3)))
if you want the found cell's contents of Chad in E2:
=INDIRECT(ADDRESS(SUMPRODUCT((A1:C3=E1)*ROW(A1:C3)),SUMPRODUCT((A1:C3=E1)*COLUMN(A1:C3))))
How things work:
SUMPRODUCT returns in this case the sum of the products between a boolean array of TRUE (searched value found in cell) and FALSE (searched value not found in cell) for every cell in the table and the corresponding row/column (absolute) numbers of those cells in the sheet; thus, the result is essentially the row/column (absolute) number of the cell where the value has been found, since TRUE=1 and FALSE=0 in mathematical terms
ADDRESS returns a cell's address as text (not as reference!)
INDIRECT returns the reference corresponding to a cell's text address
Source and credit goes to: this answer by XOR LX. Could have added the link in a comment, mentioning the duplicate question, but I wanted to expand and explain the answer a little bit, therefore more characters were needed.
Assuming you're using Excel 2007 and above.
You will need a helper column. If your table looks like in your example, in cell D1 write:
=IFERROR(MATCH($E$1,$A1:$C1,0),0)
And drag it down. Then in cell E1 write your search value ("Chad" for instance). Then you have your search result in cell E2 with this formula:
=IF(MAX($D:$D)=0,NA(),MATCH(MAX($D:$D),$D:$D,1)&","&MAX($D:$D))
If you want a simpler solution, it is possible to use only one helper (or not at all, at the cost of a complicated formulae).
Let's say I take your example. I will use the D column to display result :
In D1, I put the name I want to find : Chad
In D2, I put the helper that will return an Index of the value searched (-1 if not found) : =IFERROR(MATCH(D1,SPLIT(TEXTJOIN(";",TRUE,A1:C3),";"),0),-1)
In D3, I put the formulae to get the row,column value (FALSE if not found) : =IF(D2<>-1,ROUNDUP(DIVIDE(D2,COLUMNS(A1:C3))) & "," & IF(MOD(D2,COLUMNS(A1:C3))=0,COLUMNS(A1:C3),MOD(D2,COLUMNS(A1:C3))))
If you really want to use only one formulae, it is possible in D3 to replace all references to D2 by the formulae used in D2.
This formula returns the row and column number of a given value in a two-dimensional array.
=LET(
array, B2:D4,
findvalues, C7,
arrayrows, ROWS(array),
arraycols, COLUMNS(array),
rowindex, SEQUENCE(arrayrows*arraycols,,1,1/arraycols),
colindex, MOD(SEQUENCE(arrayrows*arraycols,,0),arraycols)+1,
flatarray, INDEX(array,rowindex,colindex),
valueflatindex, MATCH(findvalues,flatarray,0),
valuerow, ROUNDUP(valueflatindex/arraycols,0),
valuecol, MOD(valueflatindex-1,arraycols)+1,
absvaluerow, MIN(ROW(array))+valuerow-1,
absvaluecol, MIN(COLUMN(array))+valuecol-1,
CHOOSE({1,2},absvaluerow,absvaluecol)
)
A B C D E
1
2 John Matt Pete
3 Sara Bret Chad
4 Lila Maya Cami
5
6
7 find: Chad
8 formula: 3 4
More precisely, this formula scans a given array row by row and returns the address of the first occurrence of a given value.
If you need the row and column numbers relative to the array's top left cell, then in CHOOSE(...), instead of absvaluerow/absvaluecol, use valuerow/valuecol.
If you want the values to be comma separated and in one cell, instead of CHOOSE(...), use absvaluerow & "," & absvaluecol
If your Excel version does not support the latest functions, such as LET, the formula should still work if you rewrite it so that it does not use the LET variables.
Find Multiple Values
You can also find multiple values in an array using this formula as explained in my answer in this thread.

Looking for an alternative to MAX(COUNTIF(Range, Criteria)) that can ignore white-space

I currently have an excel formula that can search a comma separated list string contained in a cell and determine whether that cell contains another exact string as an element.
The formula is: MAX(COUNTIF(A1,{"*,";""}&B1&{"",",*"}))
The formula behaves, correctly, like so:
A B Result Comment
1,2,3 1 1 1 exists in 1,2,3
1,2,3 2 1 2 exists in 1,2,3
A,B,C C 1 C exists in A,B,C
A,B,C Z 0 Z does not exist in A,B,C
123,456,789 5 0 5 does not exist in 123,456,789
The problem I've encountered with this is that the formula fails as soon as whitespace is introduced. Since COUNTIF must be passed a range as it's first parameter, I cannot simply use:
SUBSTITUTE(A1," ","")
I'd also prefer, if at all possible, not to use any extraneous cells to hold the whitespace eliminated string.
It would be very simple to write a function in vba to do this, but as this spreadsheet is to be distributed to end-users (who might inadvertently disable macros), this is not an ideal solution.
Does anyone know of any alternative formula to the one proposed above?
=SEARCH("," & B1 & ",", "," & SUBSTITUTE(A1," ","") & ",")
but this approach will fail if any of your strings to be found contain spaces
=IF(ISNUMBER(SEARCH(B1,A1,1))=TRUE,"Text True","Text false")
Does this solve your problem? It searches the text from column A and return text result.

Resources