Concatenate permutations between two columns - excel

I need help with an excel assignment.
Name City
---------------
John London
Maxx NY
Ashley DC
Paris
Solution for this must be:
John-london
John-NY
John-DC
John-Paris
Maxx-london
Maxx-NY
.
.
.
.so on.
Simply I have to add text of all elements in one column to text of all elements in other column. I will appreciate if a solution without macros or VB is provided.

You can use this formula (start in Row 1 and fill down until you run out of combinations):
=IFERROR(INDEX(L_1, CEILING(ROW()/COUNTA(L_2),1) ,1) & "-" &
INDEX(L_2, 1+MOD(ROW()-1, COUNTA(L_2)) ,1), "That's it!")
I'm using named ranges "L_1" and "L_2" to refer to the first and second lists respectively

Here's an Array Formula you can use, though you will need to modify the size of the matrix depending on how many entries you have
=CONCATENATE(INDEX(A:A,MMULT(ROW(A1:A3),TRANSPOSE(ROW(B1:B4))/TRANSPOSE(ROW(B1:B4)))),"-",INDEX(B:B,MMULT(ROW(A1:A3)/ROW(A1:A3),TRANSPOSE(ROW(B1:B4)))))
Assuming Column A is Names and Column B is Cities, you would select 12 cells (3 rows high, 4 columns wide), paste the above formula in the first cell and press Ctrl + Shift + Enter to create the array formula.
If you want to see a little simpler version to see what it does before the INDEX references, check with the same area:
=CONCATENATE(MMULT(ROW(A1:A3),TRANSPOSE(ROW(B1:B4))/TRANSPOSE(ROW(B1:B4))),"-",MMULT(ROW(A1:A3)/ROW(A1:A3),TRANSPOSE(ROW(B1:B4))))
Here's a screenshot (with the formula split in 2 lines) of the single formula displaying the output over multiple cells:

This is a simple example in VBA. It is intended to show the concept, not the best practices. Please use it to get you started, and get back here if you need more help, if you want to improve the performances, etc.
The example assumes that the two lists are in A1:An and B1:Bm, and the resulting list goes in column C.
Sub Test()
Dim R1 As Integer, R2 As Integer, R As Integer, NR As Integer
NR = ActiveSheet.UsedRange.Rows.Count
Columns(3).Clear
For R1 = 1 To NR
If Not IsEmpty(Cells(R1, 1)) Then
For R2 = 1 To NR
If Not IsEmpty(Cells(R2, 2)) Then
R = R + 1
Cells(R, 3) = Cells(R1, 1) & " - " & Cells(R2, 2)
End If
Next R2
End If
Next R1
End Sub

resulting column formula should be
=column1&"-"&column2

Related

How to merge two vertical number lists into one long vertical list in Excel?

So I have a few columns with numbers and I'm wondering how to make a dynamic combined list of them all? I know consolidate could theoretically work but my lists change and I don't think consolidating is dynamic. The 'Combines List' on the right is what I hope to accomplish. I want all duplicates but I don't want any spaces because for example Column C could go from having 3 numbers to 5 numbers easily. Also sorting the list would be a super bonus as that's my final goal so if I can go about this another way let me know.
Try, with O365:
=FILTERXML("<t><s>" & TEXTJOIN("</s><s>",TRUE,TRANSPOSE(numBers)) & "</s></t>","//s")
If you want the list sorted, then:
=SORT(FILTERXML("<t><s>" & TEXTJOIN("</s><s>",TRUE,TRANSPOSE(numBers)) & "</s></t>","//s"))
EDIT If you are running on the MAC, or online Excel, you do not have the FILTERXML function. You can use the following formula instead:
=SORT(--TRIM(MID(TEXTJOIN(REPT(" ",99),TRUE,TRANSPOSE(C10:F29)),(SEQUENCE(COUNT(numBers))-1)*99+1,99)))
This formula will work for a bit more than 300 or so characters (digits), after which you will run into the 32,767 character limit for TEXTJOIN. The first formula will have a limit of a bit more than 3,000 numbers. If you might have more than that, you should look at a VBA solution
numBers is a named range larger than what you need. Blanks will be ignored. In this case, I use c10:f100, but you can increase the size to suit.
with sort
Since you are using Office 365, try this small User Defined Function:
Public Function stackum(rng As Range)
Dim wf As WorksheetFunction
Set wf = Application.WorksheetFunction
Dim arr, i As Long, brr, b
ReDim arr(1 To wf.CountA(rng), 1 To 1)
brr = rng
i = 1
For Each b In brr
If b <> "" Then
arr(i, 1) = b
i = i + 1
End If
Next b
stackum = arr
End Function
As you see, it spills down dynamically.
If you want a formula rather than a UDF, try:
=LET(z,INDEX($C$10:$F$18,MOD(SEQUENCE(4*9)-1,9)+1,ROUNDUP(SEQUENCE(4*9)/9,0)),FILTER(z,z<>""))
It also spills down. I don't like this formula. It relies on the "magic number" 9 which is the height of the input table.
EDIT#1:
To eliminate both magic numbers (4, 9) we can use:
=LET(tb,$C$10:$F$18,cl,COLUMNS(tb),rw,ROWS(tb),z,INDEX(tb,MOD(SEQUENCE(cl*rw)-1,9)+1,ROUNDUP(SEQUENCE(cl*rw)/rw,0)),FILTER(z,z<>""))
Formula solution for all Excel version
1] "Combine List with sort" in H10, formula copied down :
=IFERROR(SMALL($C$10:$F$18,ROW(A1)),"")
2] "Combine List with sort & remove duplicate" in I10, formula copied down :
=IF(ROW(A1)<=SUMPRODUCT(($C$10:$F$18<>"")/COUNTIF($C$10:$F$18,$C$10:$F$18&"")),SMALL($C$10:$F$18,COUNTIF($C$10:$F$18,"<="&I9)+1),"")

How to automatically fill down by multiple?

I have a column of data that need to be filled. The formula should go like:
SUM(A10 + B10)
SUM A20 + B20
SUM A30 + B30
. .
. .
I have no idea on how to do the setup. Appreciate for any help :)
You're looking for the INDIRECT worksheet function. You need to nest it within the SUM function and you'll get what you're after. E.g. assuming you're in a cell in the very first row on a worksheet, you type:
=SUM(INDIRECT("A" & ROW()*10), INDIRECT("B" & ROW()*10))
One option is to use INDEX function here, it's not volatile like INDIRECT and will still work if you insert rows or columns, e.g. in cell C2 use this formula copied down
=SUM(INDEX(A$1:B$1000,ROWS(C$2:C2)*10,0))
.....or alternatively, this method will actually give you the formula =SUM(A10,B10) in the first cell and =SUM(A20,B20) in the next cell etc.
Put this formula in C2 and copy down as far as required
="=SUM(A"&ROWS(C$2:C2)*10&",B"&ROWS(C$2:C2)*10&")"
Select whole range > Right Click > Copy > Right Click > Paste Special > Values > OK > ENTER
That creates text versions of the required formulas - to convert to actual formulas do an "Edit/Replace" and replace = with =
Use =SUM(A10,B10) in the first cell and the drag the cell content to all the below cells if you want to fix a attribute like column number than put a $ symbol in front of it eg =SUM($A10,$B10). Similarly, for rows use =SUM(A$10,B$10).
I would probably do something like this, only because I prefer VBA.
Sub FillSheet()
Dim j, k
j = 10
k = 1
For j = 10 to 500 Step 10 '<<--Starts at 10, then 20, 30, etc up to 500
Worksheets("YourWorkSheetName").Range("A" & k).Formula = "=SUM(A" & j & ":B" & j & ")"
k = k +1
Next j
End Sub
Modify according to your requirements. Change "A" if want the formula in another column. Change "500" to however many lines you need.

counting multiple instances of a number in a range

I have a range of numbers and I need to identify if the first number of each cell is repeated anywhere in the corresponding row.
For example, in row 2 below, column 2 and column 3 both start with a 3. I know that if I do =LEFT(TRIM(cell)) to get just the first number but how do I find the rows that have repeated numbers in the row so row 1 isn't marked but row 2 is?
100|600|203|700| |
202|302|301|400|600|
Use a helper column with this as an array formula:
=SUMPRODUCT(--(COLUMN($A1:$E1)<>MATCH(INT($A1:$E1/100),INT($A1:$E1/100),0)))>0
Being an array formula it must be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode. If done correctly then Excel will put {} around the formula.
Consider the following UDF():
Public Function AnyDups(rng As Range) As Boolean
Dim valu() As String
Dim i As Long, L As Long
L = rng.Count
ReDim valu(1 To L)
AnyDups = False
If L = 1 Then Exit Function
i = 1
For Each r In rng
valu(i) = Left(r.Value, 1)
i = i + 1
Next r
For i = 1 To L - 1
v = valu(i)
For j = i + 1 To L
If v = valu(j) Then AnyDups = True
Next j
Next i
End Function
For example:
The code just loops through the possible combinations of left-most characters in the cells.
It should work with either text or numeric data.
One way to do it would be to use this formula as a basis:
=IF(ISERROR(FIND(LEFT(TRIM(A1),1),B1)),FALSE,"Row "& ROW(A1))
Depending on how you want to check your row, you can adapt it. You could either have one formula to check one cell (Lock the A1 reference and drag right) - which would allow you to know where the match is but take more space on the sheet.
Or, if you don't have too many cells to check in each row, you could concatenate all cells in the same formula:
=IF(ISERROR(FIND(LEFT(TRIM(A1),1),B1&C1&D1&E1)),FALSE,"Row "& ROW(A1))
I'm sure Gary's Student will have a more elegant answer though!

Interspersing Cells From Different Columns in Excel

I have a rather simple Excel problem that I cannot seem to solve using the fill handle. I have spent two days developing complex formulas to extract and format hexadecimal test data but I'm stuck on what seems a trivial problem. It is best described with an image(I need 10 rep to post directly):
I have a pair of columns of 128 rows, each row across the columns contains consecutive data entries. I want to arrange the data in a single in-order column, as show above. I tried manually entering a series of functions in column D (=A1,=B1,=A2,=B2 etc.) for the first several rows and using the fill handle, but it ends up skipping large chunks of the data.
There must be a simple solution for this sort of rearrangement but I cannot work it out!
In D1 enter
=INDIRECT("a"&QUOTIENT(ROW()+1,2))
then in D2 enter
=INDIRECT("b"&QUOTIENT(ROW()+1,2))
then select D1:D2 and drag the fill handle in D2 down to twice as many rows you have in columns A:B.
Try =INDEX($A$1:$B$128,INT((ROW()+1)/2),ABS(MOD(ROW(),2)-2)). Put in C1 and fill down. If you are putting it somewhere else that doesn't begin at row 1, subtract (start row - 1) from each call to ROW().
in vba, just increment your variables before ending the for, next loop like so:
Sub crundle()
i = 1
j = 1
For i = 1 To 128
ActiveSheet.Range("d" & i) = "=a" & j
ActiveSheet.Range("d" & i + 1) = "=b" & j
i = i + 1
j = j + 1
Next i
End Sub

Sort values in an Excel spreadsheet

I've got a spreadsheet full of names and peoples' roles, like the one below:
Role Name Change
1 A Yes
2 A No
5 A N/Ap
1 B Yes
3 B No
2 C Yes
4 C No
I have to come up with a spreadsheet like the one below:
1 2 3 4 5 6
A Yes
B
C
Basically, it should retrieve the information from the first spreadsheet and be layed out clearly on the second one.
There are way too many names and roles to do it manually. VLMOVE won't work and I've tried MATCH and INDEX.
Alternative to #RocketDonkey (but thanks for more complete desired result!) could be to string together Role and Name (say in a column inserted between B & C in Sheet1 [because I think OP wants a separate sheet for the results]):
C2=A1&B2 copied down as required
then use a lookup in Sheet2!B2:
=IFERROR(VLOOKUP(B$1&$A2,Sheet1!$C$2:$D$8,2,FALSE),"")
copied across and down as required.
This assumes the grid for the results (as in question) has been constructed (and that there are 7 rows with data - adjust $8 as necessary otherwise.)
Agree with #Melanie that if you can force your data into a structure that can be interpreted as numbers (1 being yes, 0 being false, for example), Pivot tables are far and away the easiest way (since they will display numbers as the values - not the text). *(see below)
If you want to display arbitrary text, you could try this:
=IF(
SUMPRODUCT(--($A$2:$A$8=F$1),--($B$2:$B$8=$E2),ROW($A$2:$A$8))=0,"",
INDEX(
$A$1:$C$8,
SUMPRODUCT(--($A$2:$A$8=F$1),--($B$2:$B$8=$E2),ROW($A$2:$A$8)),
3))
This checks to see if the SUMPRODUCT of the three columns totals 0 (which will happen when no combo of x/y is matched (like Name: C, Role: 5, for instance), and if so, it returns "". Otherwise, it returns the value in column Value.
*A ‘pivot table option’ would be to represent the Change as a number (eg as formula in D2 copied down). Then create a pivot table from (in the example) A1:D8, with fields as shown. Copy the pivot table to a different sheet with Paste Special/Values (though shown in F11:K15 of same sheet in example). Then in that other sheet select row starting with Name A and as far down as required, Replace -1 with No, 1 with Yes and 0 with N/Ap.
AMENDED
You can use array formulas to reorganize your table, without having to change the its structure. Assuming the data is in the range A2:C8 on Sheet1 and the result table is to be in range A1:G4 on Sheet2, the following formula would be the first entry (role 1 and name A) in the result table.
=IFERROR(INDEX(Sheet1!$A$2:$C$8,MATCH(B$1&$A2,Sheet1!$A$2:$A$8&Sheet1!$B$2:$B$8,0),3),"-")
The MATCH formula returns the row number in which the role/name combination 1A occurs. The INDEX function returns the contents of the cell at the row number found by the MATCH formula and the column number 3, i.e., the Change column of your data table. The IFERROR returns "-" if the role/name combination is not in the data table.
Be sure to enter the formula using the Control-Shift-Enter key combination. Then copy the formula to the remaining cells of the result table.
The data table on Sheet1:
The result table on Sheet2:
Well since there's Excel-VBA tag, thought it would complete the solutions types by adding one in VBA :) The following code is not elegant, in any case you need to use code base, give it a try :)
Code:
Option Explicit
Public Sub sortAndPivot()
Dim d As Object
Dim ws As Worksheet
Dim sourceArray As Variant, pvtArray As Variant, v As Variant
Dim maxRole As Long
Dim i, j, k, m As Integer
Set d = CreateObject("Scripting.Dictionary")
Set ws = Worksheets("Sheet3") '-- set according to your sheet
'-- you could enhance by using an input box to select the range
sourceArray = Application.WorksheetFunction.Transpose(ws.Range("B3:D9").Value)
'-- max role number
maxRole = Application.WorksheetFunction.Max(ws.Range("B3:B9"))
'-- find unique name list
For i = LBound(sourceArray, 2) To UBound(sourceArray, 2)
If Not d.exists(sourceArray(2, i)) Then
d.Add sourceArray(2, i), i
End If
Next i
ReDim pvtArray(d.Count, maxRole)
pvtArray(0, 0) = "Name"
'-- add unique names from dictionary
j = 1
For Each v In d.keys
pvtArray(j, 0) = v
j = j + 1
Next
'-- add unique Role number list
For i = UBound(pvtArray, 2) To LBound(pvtArray) + 1 Step -1
pvtArray(0, i) = i
Next i
'-- sort into the correct positions
For k = LBound(pvtArray, 1) + 1 To UBound(pvtArray, 1)
For m = LBound(pvtArray, 2) + 1 To UBound(pvtArray, 2)
For i = LBound(sourceArray, 2) To UBound(sourceArray, 2)
If pvtArray(k, 0) = sourceArray(2, i) Then
If pvtArray(0, m) = sourceArray(1, i) Then
pvtArray(k, m) = sourceArray(3, i)
End If
End If
Next i
Next m
Next k
'Output the processed array into the Sheet in pivot view.
Range("F2").Resize(UBound(pvtArray) + 1, _
UBound(Application.Transpose(pvtArray))) = pvtArray
Set d = Nothing
End Sub
Results:
There is another way to go about it without VBA. If you create another column that concatenates the first two in the first spreadsheet, like so:
Role Name Change CheckColumn
1 A Yes 1A
2 A No 2A
5 A N/Ap 5A
1 B Yes 1B
3 B No 3B
2 C Yes 2C
4 C No 4C
Then you can use the Offset and Match functions together to find the change in the 2nd sheet. So assuming your data is laid from cell A1, the formula in cell B2 would be:
=iferror(offset(Sheet1!$A$1,match(B$1&$A2,sheet1!$D:$D,0),2),"")
Alternatively, if you put the concatenated column in sheet1 before the role column, you can use vlookup in sheet2, with the formula being:
=iferror(vlookup(B$1&$A2,sheet1!$A:$D,4,false),"")

Resources