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),"")
Related
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!
I have 2 tabs of data with a unique identifier. The identifier is not in any particular order. I need my vlookup / index / match to show me all the identifiers that are not present in tab 2.
Reason: I am working where the systems they used failed a data transfer. I have to see what data there was compared to what data is currently on the system. Any data that is missing, i will need to add to the new system.
Example;
Tab1 Column A:
123456,
654321,
789456,
456789.
Tab2 Column B:
654321,
123456,
456789.
In Tab 3, I want excel to tell me that 789456 is not present in Tab 2.
As you can see in the above example, the unique identifier could be in any order, therefore i cannot put both columns side by side and ask to do a match between the 2 - i need it to look through the whole column.
All the tutorials i have seen assume that column A matches in order of column B
I have 70,000 rows to go through.
Any help would be appreciated.
Thanks in advance.
To do it with a formula you will want a helper column in the First tab.
In an empty column, I used column B, put the following in the second row:
=IF(ISERROR(VLOOKUP(A2,Sheet2!B:B,1,FALSE)),MAX($B$1:B1)+1,"")
This will create a column of numbers that increment on the ones not found in sheet two.
At this point you can simply filter on the new column for anything that in non blank and get your list.
If you want to do it with a formula in the Third tab then use this formula that refers to the helper column on the first tab:
=IFERROR(INDEX(Sheet1!A:A,MATCH(ROW(1:1),Sheet1!B:B,0)),"")
Then copy/drag down sufficient to get blanks.
With 70,000 items I would avoid array formulas as it will slow the calculation down and may even crash excel.
You could try using something like this:
=IFERROR(VLOOKUP(<value cell>, 'Tab2'!B:B, 1, FALSE), FALSE)<>FALSE
Copy all the values from tab 1 column A into tab 3 column A. In tab 3 column B, paste the above formula in every row where there is a value in column A, using referencing the cell from column A and the same row as the value cell. The formula will attempt to look up the value from tab 1 in tab 2. If it is missing, it will generate an error which is caught by the IFERROR function, which will return FALSE instead of letting the error escape. Finally, that FALSE is negated to return TRUE if the value is present in tab 2, and FALSE if the value is missing in tab 2.
From this point you can use a column filter in tab 3 to only see those rows with a TRUE value, that will only show you values that are present in both tab 1 and tab 2.
Soulution for this is COUNTIF() the formula would be:
=COUNTIF(Sheet1!A:A,Sheet2!A1)
After applying that for all rows, just filter those that have value 0.
This macro will produce a compact list in Sheet3:
Sub WhatsMissing()
Dim s1 As Worksheet, s2 As Worksheet, s3 As Worksheet
Dim r1 As Range, N As Long, K As Long, i As Long
Dim v As Variant
Set s1 = Sheets("Sheet1")
Set s2 = Sheets("Sheet2")
Set s3 = Sheets("Sheet3")
Set r2 = s2.Range("B:B")
K = 1
N = s1.Cells(Rows.Count, "A").End(xlUp).Row
With Application.WorksheetFunction
For i = 1 To N
v = s1.Cells(i, "A").Value
If .CountIf(r2, v) = 0 Then
s3.Cells(K, "A").Value = v
K = K + 1
End If
Next i
End With
End Sub
I have a table with multiple columns, the first column contains index values (0,1,2...N), think cluster ID's, and the remaining columns contain text strings, call these labels. The number of columns varies per row, but always contains at least one cell with a label, singular clusters. I have another table that contains a tag for each label, ie two columns with the first as the label and the second as the tag. I would like to have a table with index values and tags. Basically I need a formula that returns the row reference if a label is found in that row. I initially though some INDEX-MATCH combination or VLOOKUP would work, but all the examples I found have only one column in their search criteria, which is known before hand. If anyone can help it would be much appreciated.
I have tried replacing the labels with their tags, but one label can have multiple tags, so that did not work. Thnx in advance.
EDIT:
#Ben1344 almost has the right example, below is a small example ( I apologise for my naming convention)
Table 1
0 A C D
1 E G
2 Z F X
3 Y
Table 2
A foo
C foo
D bar
E pikachu
F psyduck
G jigglypuff
X goldeen
Y psyduck
z foo
Desired Table
0 foo
0 foo
0 bar
1 pikachu
2 psyduck
1 jigglypuff
2 goldeen
3 psyduck
2 foo
It sounds like you have 2 tables and would like a third.
From your example, it looks like your desired table is effectively an additional column in your second table. The tags are largely a distraction. What needs to be done is:
Given table 1 and a label, get the index.
I can see two ways to approach this. OPTION 1:
VBA User Defined Function add the following routine to a module in VBA
Public Function FindInColumns(FindMe As Range, LookIn As Range) As Long
Dim val
val = FindMe.value
Dim rng As Range
For Each rng In LookIn
If rng.value = val Then GoTo ExitLoop
Next rng
ExitLoop:
FindInColumns = rng.Row - LookIn.Row + 1
End Function
Assume table 1 is at A1:D4
Assume table 2 is at F1:G9
Put Table 3 at I1:J9
In cell J1 put =G1
In cell I1 put the following formula:
=INDEX($A$1:$D$4,FindInColumns(F1,$B$1:$D$4))
And drag down to the rest of the cells
OPTION 2:
(from: here)
Using only excel formulas:
Assume table 1 is at A1:D4
Assume table 2 is at F1:G9
Put Table 3 at I1:J9
In cell J1 put =G1
in cell I1 put the following formula:
=INDEX($A$1:$A$4,MIN(IF($B$1:$D$4=G1,ROW($B$1:$D$4)-ROW($B$1)+1)))
This is an Array formula so when you enter it hit CTRL+SHIFT+ENTER instead of just ENTER to confirm.
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
My data looks like:
Colums C,D,E,F
Row 1 4,10,40,F
Row 2 4,12,48,F
Row 3 4,14,56,F
Row 4 3,16,48,F
Row 5 1,18,18,F
Row 6 1,20,10,F
Row 7 0,22,0,0
My foucs is on column F. My current array formula in column F is:
=IF(C31=0,VLOOKUP(INDEX($C$30:$C$38,MIN(($C$30:C30*D31)-$E$30:E30)),$C$30:$E$38,1),"F")
In case column C is is equal to 0, I want the formula of column F to return the value of column C,which has the respective minimum difference between column C (allrowsbefore) * column D (currentrow) and E(respective, all rows before). For instance, in row 2 that minimization problem would equal:C1*D2-E1, if C2 would be equal to 0.
However, now i want column F formular to only choose a value of C which is unequal to 0.
Hence, in column F7 i want column F to give me the value 1, as this is a C value which is bigger as 0 and has the respective min difference between (C6*D7)-E6.
(For value 4 of C it would be 32 and for value 3 of C it would be 18).
How and where do i include the if condition, that the value of C chosen has to be bigger as 1, into my existing formula ?
Thanks a lot for helping out, its much appreciated!!! In case i need to split up my existing formula that would be alright, too.
I reread your question more carefully after making my earlier comment, and I think I understand better now. To make sure you get the smallest value greater than 0, you use the following according to this KB article:
=MIN(IF(A1:A10>0,A1:A10))
So your formula in F7 should look like this for the ranges you described:
=IF(C7=0,VLOOKUP(INDEX($C$1:$C$7,MIN((IF($C$1:$C$7>0,$C$1:$C$7)*D6)-$E$1:$E$7)),$C$1:$E$7,1),"F")
EDIT
Okay. I think I finally understand the question. :)
If Cx <> 0, Fx = Cx
If Cx = 0, Fx = the address of the cell in Column C that produces minimum of (C1 * D7 - E1, C2 * D7 - E2, ..., CN * D7 - EN)
From what I know of Excel, I don't think you can do this with a single formula. INDEX and VLOOKUP both look for values in the worksheet, but you're trying to work from the value of a calculation (MIN). Using the array formula to find that value is really smart. But once we get the value (4 in this case), we can't use that to look up the address of the cell in column C that produced that result. I really can't think of any way to do this without a macro. Are you able to do that?
EDIT: Macro Solution
This is a little quick and dirty, but it passed the test. Make sure to save your workbook as a Macro-Enabled workbook. Hit Alt+F11 to open the macro editor. You might need to insert a new module. You can do this by right-clicking the workbook project in the project explorer on the left.
Once you've done this, double-click on the module. You should get a blank white text editor on the right. Paste the following code into that window:
Public Function MinimumC()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp As Long
Dim rngC As Range
Set rngC = ActiveSheet.Range("C1:C" & rngCurrent.Row - 1)
For Each c In rngC.Cells
If c.Value2 <> 0 Then
tmp = c.Value2 * rngCurrent.Offset(0, -2).Value2 - c.Offset(0, 2)
If tmp < minimum Then
minimum = tmp
Set rngMin = c
End If
End If
Next c
MinimumC = rngMin.Value2
End Function
Save the workbook. You can now close the code editor.
Now you can use the following formula in F1 and copy down column F: =IF(C1<>0,C1,MinimumC()).
This will work as long as the value of C1 * Dy - E1 is never greater than 100000000. :)