Code to fill matrix with 'yes' or 'no' based on input - vba

I have a matrix in an Excel sheet. In the first column are names of computers and in the other rows, I have users who are using it. For each computer there could be one associated user or two users and so on.
I wish to create a matrix of computers in the column and the all the users in the row and have VBA code to search the sheet, and if the user uses that computer, the output should be yes, else no.
Main Sheet
Computer A Dev Priya Rakesh Joseph
Computer B Rakesh Joseph
Computer C John Nisha Dev
Output Sheet
Computers Dev Priya Rakesh Joseph John Nisha
Computer A Y Y Y Y N N
Computer B N N Y Y N N
Computer C Y N N N Y Y

Rename sheet to 'Main' and copy data to it start from range A1.
Beware blank cell because I check end of row and column by check cell is "".
Rename other sheet to 'Output'.
Copy my code then run.
Note: Output sheet will clear all the time you run this macro.
Sub createMatrix()
Dim i As Long
Dim j As Long
Dim k As Long
Dim rngFind As Range
' Clear all contents in sheets output
Sheets("Output").Activate
Sheets("Output").Cells.ClearContents
i = 0
j = 1
k = 1
Do While Sheets("Main").Range("A1").Offset(i).Value <> ""
' Insert computer name to output sheet
Sheets("Output").Range("A2").Offset(i).Value = Sheets("Main").Range("A1").Offset(i).Value
Do While Sheets("Main").Range("A1").Offset(i, j).Value <> ""
' Check name is exists?
Set rngFind = Rows("1:1").Find(what:=Sheets("Main").Range("A1").Offset(i, j).Value, LookAt:=xlWhole)
If rngFind Is Nothing Then
' If not exists paste new name
Sheets("Output").Range("A1").Offset(0, k).Value = Sheets("Main").Range("A1").Offset(i, j).Value
' Mark use as 'Y'
Sheets("Output").Range("A1").Offset(i + 1, k).Value = "Y"
k = k + 1
Else
' Mark use as 'Y'
rngFind.Offset(i + 1).Value = "Y"
End If
j = j + 1
Loop
i = i + 1
j = 1
Loop
' This loop for Mark 'N'
i = 0
j = 1
Do While Sheets("Output").Range("A2").Offset(i).Value <> ""
Do While Sheets("Output").Range("A1").Offset(0, j).Value <> ""
' If found blank cell Mark 'N'
If Sheets("Output").Range("A2").Offset(i, j).Value = "" Then
Sheets("Output").Range("A2").Offset(i, j).Value = "N"
End If
j = j + 1
Loop
i = i + 1
j = 1
Loop
End Sub
Sample main sheet and output

This version creates a new sheet
Option Explicit
Public Sub TheMatrixReloaded() 'There is no spoon
Const FR As Long = 1: Const FC As Long = 2
Dim ws1 As Worksheet, ws2 As Worksheet, lr As Long, ur As Range
Dim ud As Object, cel As Range, i As Long
Set ws1 = ThisWorkbook.Worksheets("Sheet1")
With ws1.UsedRange
lr = ws1.Cells(.Rows.Count + .Row + 1, FC - 1).End(xlUp).Row
Set ur = ws1.Range(ws1.Cells(FR + 1, FC), ws1.Cells(lr, .Columns.Count + .Column - 1))
End With
Set ud = CreateObject("Scripting.Dictionary")
Application.ScreenUpdating = False
Set ws2 = ThisWorkbook.Worksheets.Add(After:=ws1)
ws1.Range(ws1.Cells(FR, FC - 1), ws1.Cells(lr, FC - 1)).Copy ws2.Cells(FR, FC - 1)
For Each cel In ur
With cel
If Len(.Value2) > 0 Then
If Not ud.Exists(.Value2) Then
ud.Add .Value2, FC + i
ws2.Cells(FR, FC + i).Value2 = .Value2
ws2.Cells(.Row, FC + i).Value2 = "Y": i = i + 1
Else
ws2.Cells(.Row, ud(.Value2)).Value2 = "Y"
End If
End If
End With
Next
With ws2.UsedRange
Set ur = ws2.Range(ws2.Cells(FR + 1, FC), ws2.Cells(.Rows.Count, .Columns.Count))
Set ur = ur.SpecialCells(xlCellTypeBlanks)
End With
ur.Value2 = "N": ur.Font.Color = RGB(177, 177, 177)
ws2.Columns(1).AutoFit: ws2.UsedRange.HorizontalAlignment = xlCenter
ws2.Rows(2).Insert Shift:=xlDown, CopyOrigin:=xlFormatFromRightOrBelow
Application.ScreenUpdating = True
End Sub

Related

Macro to move rows to another sheet based on cell value

The below macros works for - I have a workbook with two sheets (Active and Archive). And in Active sheet i have AB column that contains Active or Archive status. If its status Archive macros cuts and moves the row to the sheet Archive. This macros works perfect.
Now i need to add some other sheets to excel and named them (New, Accepted, Rejected) and of course i add the same status to the column AB. Now i want macros to do the same if AB = Archive or New or Accepted or Rejected cut and move the row to the sheet named Archive or New or Accepted or Rejected.
I tried it by myself but can't do it.
Need ur help. Thanks in advance.
Private Sub CommandButton1_Click()
Dim x As Integer
Dim y As Integer
Dim i As Integer
Dim shSource As Worksheet
Dim shTarget1 As Worksheet
Set shSource = ThisWorkbook.Sheets("Active")
Set shTarget1 = ThisWorkbook.Sheets("Archive")
If shTarget1.Cells(2, 28).Value = "" Then
x = 2
Else
x = shTarget1.Cells(2, 28).CurrentRegion.Rows.Count + 1
End If
i = 2
Do Until shSource.Cells(i, 28) = ""
If shSource.Cells(i, 28).Value = "Archive" Then
shSource.Rows(i).Copy
shTarget1.Cells(x, 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
x = x + 1
GoTo Line1
End If
i = i + 1
Line1: Loop
End Sub
You can set up multiple variables and choose the right ones in a select case. There is some repetition here that could get cleaned up with arrays.
Sub CommandButton1_Click()
Dim x As Integer 'archive target counter
Dim y As Integer 'new target counter
Dim z As Integer 'accepted target counter
Dim w As Integer 'rejected target counter
'the above could be an array if we were trying to generalize
Dim i As Integer 'counts rows in original sheet
Dim shSource As Worksheet
Dim shTarget1 As Worksheet 'archive sheet
Dim shTarget2 As Worksheet 'new sheet
Dim shTarget3 As Worksheet 'accepted sheet
Dim shTarget4 As Worksheet 'rejected sheet
'these 4 could also be an array, as could their names, in which case some things become loops and the select case could be written out
Set shSource = ThisWorkbook.Sheets("Active")
Set shTarget1 = ThisWorkbook.Sheets("Archive")
Set shTarget2 = ThisWorkbook.Sheets("New")
Set shTarget3 = ThisWorkbook.Sheets("Accepted")
Set shTarget4 = ThisWorkbook.Sheets("Rejected")
If shTarget1.Cells(2, 28).Value = "" Then
x = 2
Else
x = shTarget1.Cells(2, 28).CurrentRegion.Rows.Count + 1
End If
If shTarget2.Cells(2, 28).Value = "" Then
y = 2
Else
y = shTarget2.Cells(2, 28).CurrentRegion.Rows.Count + 1
End If
If shTarget3.Cells(2, 28).Value = "" Then
z = 2
Else
z = shTarget3.Cells(2, 28).CurrentRegion.Rows.Count + 1
End If
If shTarget4.Cells(2, 28).Value = "" Then
w = 2
Else
w = shTarget4.Cells(2, 28).CurrentRegion.Rows.Count + 1
End If
i = 2
Do Until shSource.Cells(i, 28) = ""
Select Case shSource.Cells(i, 28).Value
Case "Archive":
shSource.Rows(i).Copy
shTarget1.Cells(x, 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
x = x + 1
Case "New":
shSource.Rows(i).Copy
shTarget2.Cells(y, 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
y = y + 1
Case "Accepted":
shSource.Rows(i).Copy
shTarget3.Cells(z, 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
z = z + 1
Case "Rejected":
shSource.Rows(i).Copy
shTarget4.Cells(w, 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
w = w + 1
Case Else 'no cutting so move to next input line
i = i + 1
End Select
Loop
End Sub
EDIT: Below is the array based version that repeats itself less. Also, I found I kept overwriting my top row in the target sheets, so I added 2 (not 1) to the target counters when I initialized them. If the original was working in your context, you may switch it back.
Sub CommandButton1_Click()
Dim TargetCounters(3) As Integer
Dim TargetNames(3) As String
TargetNames(0) = "Archive"
TargetNames(1) = "New"
TargetNames(2) = "Accepted"
TargetNames(3) = "Rejected"
Dim i As Integer 'counts rows in original sheet
Dim shSource As Worksheet
Dim shTargets(3) As Worksheet
Set shSource = ThisWorkbook.Sheets("Active")
For i = 0 To 3
Set shTargets(i) = ThisWorkbook.Sheets(TargetNames(i))
If shTargets(i).Cells(2, 28).Value = "" Then
TargetCounters(i) = 2
Else 'there is stuff. Imagine for example it is in rows 2 to 7. Count will be 6. We need to start pasting in row 8
TargetCounters(i) = shTargets(i).Cells(2, 28).CurrentRegion.Rows.Count + 2 'changed this from orinal + 1
End If
Next i
i = 2
Dim MatchIndex As Integer
Do Until shSource.Cells(i, 28).Value = ""
'you could switch this case to a call on the application's match function against TargetNames
'if you take care with the case where it is not found and indexing being right and not off by 1
Select Case shSource.Cells(i, 28).Value
Case "Archive":
MatchIndex = 0
Case "New":
MatchIndex = 1
Case "Accepted":
MatchIndex = 2
Case "Rejected":
MatchIndex = 3
Case Else 'no cutting so set signal and we will move to next input line
MatchIndex = -1
End Select
If (MatchIndex = -1) Then
i = i + 1
Else
shSource.Rows(i).Copy
shTargets(MatchIndex).Cells(TargetCounters(MatchIndex), 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
TargetCounters(MatchIndex) = TargetCounters(MatchIndex) + 1
End If
Loop
End Sub

Copying entire row from one excel sheet to next empty row of another sheet

I have two sheets data and PrevErrCheck. I am checking all occurrence of variable VarVal(this variable has data in E1 cell of PrevErrCheck) in sheet data and copy entire row to sheet PrevErrCheck. But the problem I am facing here is running macro multiple times overwriting data. I would like to keep the copied rows in sheet data and whenever I run next time, it should copy to next blank row.
I am using below code currently but bit confused to how to integrate the the option to find last row on PrevErrCheck and copy lines below that
Sub PrevErrCheck()
Dim spem As Workbook
Dim PrevErrCheck As Worksheet
Dim data As Worksheet
Dim xRg As Range
Dim xCell As Range
Dim I As Long
Dim J As Long
Dim K As Long
Set spem = Excel.Workbooks("SwitchPortErrorMonitor.xlsm")
Set PrevErrCheck = spem.Worksheets("PrevErrCheck")
Set data = spem.Worksheets("data")
spem.Worksheets("PrevErrCheck").Activate
VarVal = PrevErrCheck.Cells(1, "E").Value
I = data.UsedRange.Rows.count
J = PrevErrCheck.UsedRange.Rows.count
If J = 1 Then
If Application.WorksheetFunction.CountA(PrevErrCheck.UsedRange) = 0 Then J = 0
End If
Set xRg = data.Range("X:X")
On Error Resume Next
Application.ScreenUpdating = False
J = 3
For K = 1 To xRg.count
If CStr(xRg(K).Value) = VarVal And Not IsEmpty(VarVal) Then
xRg(K).EntireRow.Copy Destination:=PrevErrCheck.Range("A" & J + 1)
PrevErrCheck.Range("X" & J + 1).ClearContents
J = J + 1
End If
Next
Application.ScreenUpdating = True
End Sub
You have J = 3 before the loop, that may be a problem. xRg.count always returns 1048576, you should use something more specific. Try this:
Set spem = Excel.Workbooks("SwitchPortErrorMonitor.xlsm")
Set PrevErrCheck = spem.Worksheets("PrevErrCheck")
VarVal = PrevErrCheck.Cells(1, "E").Value
If IsEmpty(VarVal) Then Exit Sub
Set data = spem.Worksheets("data")
spem.Worksheets("PrevErrCheck").Activate
I = data.UsedRange.Rows.Count
J = PrevErrCheck.UsedRange.Rows.Count + 1
If J = 2 Then
If IsEmpty(PrevErrCheck.Cells(1, 1)) Then J = 1
End If
' If J = 1 Then
' If Application.WorksheetFunction.CountA(PrevErrCheck.UsedRange) = 0 Then J = 0
' End If
' Set xRg = data.Range("X:X")
' On Error Resume Next
' Application.ScreenUpdating = False
' J = 3
For K = 1 To I
If CStr(data.Cells(K, "X").Value) = VarVal Then
data.Cells(K, 1).EntireRow.Copy Destination:=PrevErrCheck.Range("A" & J)
PrevErrCheck.Range("X" & J).ClearContents
J = J + 1
End If
Next
' Application.ScreenUpdating = True
End Sub

Macro to Concatenate two columns at a time in a range

I have to create a Macro which lets me Concatenate two columns at a time in a given range. For example: In range C1:Z200, I want to concatenate Column C&D, E&F, G&H and so on. How do I do it. This is my current code which only concatenate first two columns..rest remains the same.
Set Range = ActiveSheet.Range("C1:Z100")
For Each c In Range
c.Select
ActiveCell.FormulaR1C1 = ActiveCell & " " & ActiveCell.Offset(0, 1)
ActiveCell.Offset(0, 1).Activate
Selection.Clear
ActiveCell.Offset(0, 2).Activate
Next c
Try this:
Sub Concat()
Dim i As Long, j As Long
For i = 1 To 100 'number of rows
j = 1 'reset column to 1
Do While j < 25 'max number of columns (until Column Y-Z)
j = j + 2 'start from third column (Column C)
Cells(i, j) = Cells(i, j) & " " & Cells(i, j + 1) 'concat
Cells(i, j + 1).ClearContents 'clear
Loop
Next i 'next row
End Sub
Try this:
Sub ConcatAltCellsInAltCols()
Dim oW As Worksheet: Set oW = ThisWorkbook.Worksheets("Sheet11")
Dim iLC As Long: iLC = oW.Cells(1, oW.Columns.Count).End(xlToLeft).Column
Dim iLR As Long: iLR = oW.Cells(oW.Rows.Count, 3).End(xlUp).Row
Dim iC As Long
Dim iR As Long
For iR = 1 To iLR
For iC = 3 To iLC Step 2
oW.Cells(iR, iC).Value = oW.Cells(iR, iC) & oW.Cells(iR, iC + 1)
Next
Next
End Sub
Try this using a one based array for better Performance:
Code
Option Explicit
Sub Conc()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Concat") ' <== change "Concat" to your sheet name to avoid subscript error
Dim v ' variant
Dim lng As Long
Dim j As Integer ' corr.
' use one based array to get field data
v = ws.Range("C1:Z100") ' your OP range
For lng = 1 To UBound(v)
' concatenate columns C&D, E&F, G&H, ...
For j = 0 To 11
v(lng, j * 2 + 1) = v(lng, j * 2 + 1) & v(lng, j * 2 + 2)
Next j
Next lng
' write array values back (overwriting D, F, H,... with the same values)
ws.Range("C1:Z100") = v ' your OP range
End Sub

How to copy column data from one sheet and then copy that to another sheet in vba excel

I need help with this small project. What I need to accomplished this task is the following:
I have a excel file where my macro button once clicked will read the data from a sheet1 only in column A then should throw the data to another sheet2 and move every data from the sheet1 to sheet2 and display all the data to each separate column.
here is a image of the data example. in the image every circle needs to be in its own column to the new sheet2 that is only part of the data the total of the column rows is around 900.
if need more information please let me know.
here is the code I have it copy the sheet from sheet1 to sheet2 but I need the rest to work
Sub ExportFile()
Dim strValue As String
Dim strCellNum As String
Dim x As String
x = 1
For i = 1 To 700 Step 7
strCellNum = "A" & i
strValue = Worksheets("data").Range(strCellNum).Value
Debug.Print strValue
Worksheets("NewData").Range("A" & x).Value = strValue
x = x + 1
Next
End Sub
Give this a try:
Sub DataReorganizer()
Dim s1 As Worksheet, s2 As Worksheet, N As Long, i As Long, j As Long, k As Long
Dim v As Variant
Set s1 = Sheets("Data")
Set s2 = Sheets("NewData")
N = s1.Cells(Rows.Count, "A").End(xlUp).Row
For i = N To 2 Step -1
If s1.Cells(i, "A").Value = "" And s1.Cells(i - 1, "A").Value = "" Then s1.Cells(i, "A").Delete shift:=xlUp
Next i
j = 1
k = 1
N = s1.Cells(Rows.Count, "A").End(xlUp).Row
For i = 1 To N
v = s1.Cells(i, "A").Value
If v = "" Then
j = 1
k = k + 1
Else
s2.Cells(j, k).Value = v
j = j + 1
End If
Next i
End Sub
you can try this:
Sub ExportFile()
Dim area As Range
Dim icol As Long
With Worksheets("data")
With .Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).SpecialCells(xlCellTypeConstants)
For Each area In .Areas
icol = icol + 1
Worksheets("NewData").Cells(1, icol).Resize(area.Rows.Count).Value = area.Value
Next
End With
End With
End Sub

VBA: How to transform a one column full dictionary into one column per letter?

I have a full dictionary. All the words (360 000) are in one column.
I'd like to have Column B with all words starting with "a", column C with all words starting with b...
I am trying to do a loop or something... but... It is just too long.
Any tips? Or did someone already do this vba macro?
Tks,
Stéphane.
If we start with:
Running this short macro:
Sub SeparateData()
Dim N As Long, i As Long, NewCol As Long
Dim M As Long
N = Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To N
NewCol = Asc(UCase(Left(Cells(i, 1).Value, 1))) - 63
If Cells(1, NewCol).Value = "" Then
M = 1
Else
M = Cells(Rows.Count, NewCol).End(xlUp).Row + 1
End If
Cells(M, NewCol).Value = Cells(i, 1).Value
Next i
End Sub
will produce:
NOTE:
You may want to add some error capture logic to the NewCol calculation line.
EDIT#1:
This version may be slightly faster:
Sub SeparateDataFaster()
Dim N As Long, i As Long, NewCol As Long
Dim M As Long, time1 As Date, time2 As Date
N = Cells(Rows.Count, 1).End(xlUp).Row
time1 = Now
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
For i = 1 To N
NewCol = Asc(UCase(Left(Cells(i, 1).Value, 1))) - 63
If Cells(1, NewCol).Value = "" Then
M = 1
Else
M = Cells(Rows.Count, NewCol).End(xlUp).Row + 1
End If
Cells(M, NewCol).Value = Cells(i, 1).Value
Next i
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
time2 = Now
MsgBox time1 & vbCrLf & time2
End Sub
You can try something like this.
For 360k records its take about 20sec.
To create tests data i use this sub:
Sub FillTestData()
Dim t As Long
Dim lng As Integer
Dim text As String
'Start = Timer
For t = 1 To 360000
text = vbNullString
lng = 5 * Rnd + 10
For i = 1 To lng
Randomize
text = text & Chr(Int((26 * Rnd) + 65))
Next i
Cells(t, 1) = text
Next t
'Debug.Print Timer - Start
End Sub
And for separate:
Sub sep()
'Start = Timer
Dim ArrWords() As Variant
Dim Row_ As Long
LastRow = Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
ArrWords = Range("A1:A" & LastRow) 'all data from column A to array
For i = 65 To 90 ' from A to Z
Row_ = 1
For j = LBound(ArrWords, 1) To UBound(ArrWords, 1)
If Asc(UCase(ArrWords(j, 1))) = i Then
Cells(Row_, i - 63) = ArrWords(j, 1)
Row_ = Row_ + 1
End If
Next j
Next i
'other than a[A]-z[Z]
Row_ = 1
For j = LBound(ArrWords, 1) To UBound(ArrWords, 1)
If Asc(UCase(ArrWords(j, 1))) < 65 Or Asc(UCase(ArrWords(j, 1))) > 90 Then
Cells(Row_, 28) = ArrWords(j, 1)
Row_ = Row_ + 1
End If
Next j
'Debug.Print Timer - Start
End Sub
You could try:
For i = 1 To Cells(Rows.count, 1).End(xlUp).Row
Range(UCase(Left$(Cells(i, 1).Text, 1)) & Rows.count).Offset(0, 1).End(xlUp).Offset(IIf(Range(UCase(Left$(Cells(i, _
1).Text, 1)) & Rows.count).Offset(0, 1).End(xlUp).Row = 1, 0, 1), 0).Value = Cells(i, 1).Text
Next i
Which is just building the destination address using the first letter of the word by doing the following:
Loop through each cell in column A
Get the first letter of that cell and convert it to upper case
Find the last cell in the column starting with that letter
Move over 1 column to the right
Go up until we hit the last row of data
If the last row isn't row 1, move down another row (next blank cell)
Give this cell the same value as the cell in column A that we're assessing
You can enter the following formula:
For letter A in B Column:
=IF(UPPER(LEFT(A1,1))="A",A1,"")
For letter B in C Column:
=IF(UPPER(LEFT(A1,1))="B",A1,"")
Repeat the same for letter C, D and so on..