Counting distinct values in excel - frequency function
yes I have read
Counting distinct values in excel - frequency function
I am try to count a column with different numbers
column contains (search)
1 3 7 9 5 1 3 9 4
result looking for;
C1 C2
1 = 2
2 = 0
3 = 2
4 = 1
etc
You can use COUNTIF to count the number of elements that match a condition.
Suppose you have your numbers in column A, say from A1 to A10:
A1: 1
A2: 3
A3: 7
etc...
Type in somewhere on your sheet, say in column B, the values you are interested in:
B1: 0
B2: 1
etc...
and in C1, type in
=COUNTIF($A$1:$A$10, B1)
This should count the number of values equal to B1 (i.e. 0), in A1:A10.
Enter your numbers in column A and a sequence in column B
A B
1 1
2 1
3 1
4 1
2 1
3 1
4 1
Select both columns and create a pivot table putting col A in rows. Select {COUNT} as function and you are done.
Not exactly what you are asking but i use a macro to generate frequency tables. I like it. Original code was posted by MWE at http://www.vbaexpress.com/kb/getarticle.php?kb_id=406 and i have (hopefully) improved it a bit. Have left in a little bit of redundant code so i get more replies :p
Sub zzzFrequencyDONT_SELECT_WHOLE_COLUMN()
' if user selects massive range - usually whole column - stops them
If Selection.Rows.Count > 60000 Then
MsgBox "Range selected is way too large - over 60,000. You have probably selected an entire column. Select a range of under 60,000 cells and try again"
End If
If Selection.Rows.Count > 60000 Then
Exit Sub
End If
'
' Function computes frequency count of unique values in a selection
'
Dim Count() As Integer
Dim I As Integer, J As Integer
Dim Num As Integer, NumOK As Integer, MaxNumOK As Integer, NumBad As Integer
Dim Row As Integer, Col As Integer, Temp1 As Integer, Temp2 As Integer
Dim strBuffer As String, strBadVals As String
Dim CellVal As Variant
Dim Ans As VbMsgBoxResult
Num = 0
NumBad = 0
NumOK = 0
MaxNumOK = 50
ReDim Count(MaxNumOK, 2)
strBuffer = ""
'
' sequence through each cell in selection
'
For Each Cell In Selection
Num = Num + 1
On Error Resume Next
CellVal = Cell.Value
Select Case Err
Case Is = 0
'
' no error, examine type
'
Select Case LCase(TypeName(CellVal))
Case "integer", "long", "single", "double"
'
' numeric type; if single or double, use
' Fix function to reduce to integer portion
'
If TypeName(CellVal) = "single" Or _
TypeName(CellVal) = "double" Then
CellVal = Fix(CellVal)
End If
'
' check if previously seen
' if so, simply bump counter
' if not, increment NumOK and store value
'
For I = 1 To NumOK
If CellVal = Count(I, 1) Then
Count(I, 2) = Count(I, 2) + 1
GoTo NextCell
End If
Next I
NumOK = NumOK + 1
If NumOK > MaxNumOK Then
MsgBox "capacity of freq count proc exceeded" & vbCrLf & _
"Displaying results so far", vbCritical
GoTo SortCount
End If
Count(NumOK, 1) = CellVal
Count(NumOK, 2) = 1
Case Else
NumBad = NumBad + 1
If Cell.Text <> "" Then
strBadVals = strBadVals & Cell.Text & vbCrLf
Else
strBadVals = strBadVals & "<blank>" & vbCrLf
End If
End Select
Case Is <> 0
NumBad = NumBad + 1
If Cell.Text <> "" Then
strBadVals = strBadVals & Cell.Text & vbCrLf
Else
strBadVals = strBadVals & "<blank>" & vbCrLf
End If
End Select
NextCell:
Next Cell
'
' counting done, sort data
'
SortCount:
For I = 1 To NumOK
For J = I To NumOK
If I <> J Then
If Count(I, 1) > Count(J, 1) Then
Call SwapVals(Count(I, 1), Count(J, 1))
Call SwapVals(Count(I, 2), Count(J, 2))
End If
End If
Next J
Next I
'
' store count data for display
'
Dim percentstore As Single
percentstore = Str(Count(I, 2)) / Str(Num)
For I = 1 To NumOK
strBuffer = strBuffer & Str(Count(I, 1)) & vbTab + Str(Count(I, 2)) & vbTab & FormatPercent(Str(Count(I, 2)) / Str(Num)) & vbCr
Next I
'
' display results
'
MsgBox "CTRL C to copy" & vbCrLf & _
"# cells examined = " & Str(Num) & vbCrLf & _
"# cells w/o acceptable numerical value = " & NumBad & vbCrLf & _
"# unique values found = " & NumOK & vbCrLf & _
"Frequency Count:" & vbCrLf & "value" & vbTab & "frequency" & vbTab & "Percent" & vbCr + strBuffer, vbInformation, "Frequency count - CTRL C to copy"
If NumBad > 0 Then
Ans = MsgBox("display non-numerics encountered?", vbQuestion & vbYesNo)
If Ans = vbYes Then MsgBox "Non Numerics encountered" & vbCrLf & strBadVals
End If
'
' write to worksheet?
'
' Ans = MsgBox("Ok to write out results below selection?" & vbCrLf + _
' "results will be two cols by " & (NumOK + 1) & " rows", vbQuestion + vbYesNo)
' If Ans <> vbYes Then Exit Sub
' Row = Selection.Row + Selection.Rows.Count
' Col = Selection.Column
' Cells(Row, Col) = "Value"
' Cells(Row, Col + 1) = "Count"
' For I = 1 To NumOK
' Cells(Row + I, Col) = Count(I, 1)
' Cells(Row + I, Col + 1) = Count(I, 2)
' Next I
End Sub
Sub SwapVals(X, Y)
'
' Function swaps two values
'
Dim Temp
Temp = X
X = Y
Y = Temp
End Sub
Related
I want to tag specified lines of a table for sample review.
Due to the volume of data, running repeated loops over the entire population would result in unacceptably long runtimes (as I have to tag specified sub-populations for QA sampling).
The approach I have taken is to bring in the table, and then filter based on the population I want to sample (for example, filter by location, by product, and by analyst) and then select a percentage of that population for sampling by putting "Sample" into a column.
I have tried several permutations of the code.
The first, where I used the Areas function, threw 1004 errors if there was more than one row.
The second gives strange row selections, including selecting non-hidden rows (and I can't understand why it is picking the rows that it is, as they don't seem to be correctly offset even if it was going by "all rows" not just visible rows).
ActiveSheet.ListObjects("SourceDataTable").Range.AutoFilter Field:=1, Criteria1:="Product1"
sectionCount = ActiveSheet.ListObjects("SourceDataTable").AutoFilter.Range.Columns(1).SpecialCells(xlCellTypeVisible).Cells.Count - 1
If sectionCount = 0 Then sectionSampleSize = 0 Else sectionSampleSize = Int((sectionCount / 10) + 0.5)
MsgBox ("Analyst " & analystLoopCellRef.Value & " ecomm section count is " & sectionCount & " and sample size is " & sectionSampleSize)
Do While sectionSampleSize > 0
sectionLoopRand = Int(sectionCount * Rnd + 1)
MsgBox (sectionLoopRand)
' MsgBox (ActiveSheet.ListObjects("SourceDataTable").DataBodyRange.SpecialCells(xlCellTypeVisible).Areas(1).Columns(40).Rows(sectionLoopRand).Cells(1, 1).Value)
If ActiveSheet.ListObjects("SourceDataTable").DataBodyRange.SpecialCells(xlCellTypeVisible).Columns(40).Rows(sectionLoopRand).Cells(1, 1).Value = "Sample" Then
MsgBox ("Sample overlap")
Else
ActiveSheet.ListObjects("SourceDataTable").DataBodyRange.SpecialCells(xlCellTypeVisible).Columns(40).Rows(sectionLoopRand).Cells(1, 1).Value = "Sample"
' MsgBox ("Sample address is " & ActiveSheet.ListObjects("SourceDataTable").DataBodyRange.SpecialCells(xlCellTypeVisible).Columns(40).Rows(sectionLoopRand).Cells(1, 1).Address)
sectionSampleSize = sectionSampleSize - 1
' MsgBox ("Sample selected")
End If
Loop
Older version
ActiveSheet.ListObjects("SourceDataTable").Range.AutoFilter Field:=1, Criteria1:="Product1"
sectionCount = ActiveSheet.ListObjects("SourceDataTable").AutoFilter.Range.Columns(1).SpecialCells(xlCellTypeVisible).Cells.Count - 1
If sectionCount = 0 Then sectionSampleSize = 0 Else sectionSampleSize = Int((sectionCount / 10) + 0.5)
MsgBox ("Analyst " & analystLoopCellRef.Value & " ecomm section count is " & sectionCount & " and sample size is " & sectionSampleSize)
Do While sectionSampleSize > 0
sectionLoopRand = Int(sectionCount * Rnd + 1)
MsgBox (sectionLoopRand)
' MsgBox (ActiveSheet.ListObjects("SourceDataTable").DataBodyRange.SpecialCells(xlCellTypeVisible).Areas(sectionLoopRand).Columns(40).Cells(1, 1).Value)
If ActiveSheet.ListObjects("SourceDataTable").DataBodyRange.SpecialCells(xlCellTypeVisible).Areas(sectionLoopRand).Columns(40).Cells(1, 1).Value = "Sample" Then
MsgBox ("Sample overlap")
Else
ActiveSheet.ListObjects("SourceDataTable").DataBodyRange.SpecialCells(xlCellTypeVisible).Areas(sectionLoopRand).Columns(40).Cells(1, 1).Value = "Sample"
'' MsgBox ("Sample address is " & ActiveSheet.ListObjects("SourceDataTable").DataBodyRange.SpecialCells(xlCellTypeVisible).Areas(sectionLoopRand).Columns(40).Cells(1, 1).Address)
sectionSampleSize = sectionSampleSize - 1
' MsgBox ("Sample selected")
End If
Loop
Auto filters can create non-contiguous ranges with multiple areas which can be problematic for normal range methods. One way is to loop through the visible cells and build an array of the addresses (or rows). Then by selecting an array element at random you can get the address of a cell in the visible range. For example
Option Explicit
Sub mysample()
Const TABLE_NAME = "SourceDataTable"
Const FILTER_COL = 1
Const TABLE_COL = 40 ' word sample added in table col 40
Const SAMPLE_TERM = "Product1"
Const SAMPLE_RATE = 10 ' 1 in 10 sampled
Const LOOP_MAX = 10000 ' avoid infinite while loop
Dim wb As Workbook, ws As Worksheet
Dim tbl As ListObject, rng As Range, t0 As Single
t0 = Timer
Set wb = ThisWorkbook
Set ws = wb.ActiveSheet
' apply filter and set rng to visible cells in filter col
Set tbl = ws.ListObjects(TABLE_NAME)
tbl.Range.AutoFilter Field:=FILTER_COL, Criteria1:=SAMPLE_TERM
Set rng = tbl.Range.Columns(FILTER_COL).SpecialCells(xlCellTypeVisible)
Debug.Print rng.Address, rng.Cells.Count
' build myrows array of addresses from rng.cells
Dim iCount As Integer, myrows() As String, cell As Range
iCount = -1 ' myrows(0) will be header
ReDim myrows(rng.Cells.Count)
For Each cell In rng.Cells
iCount = iCount + 1
myrows(iCount) = cell.Address
'Debug.Print iCount, cell.Address, cell.Row
Next
' determine sample size
Dim iSampleSize As Integer
If iCount > SAMPLE_RATE / 2 Then
iSampleSize = Round(iCount / SAMPLE_RATE, 0)
End If
'Debug.Print iSampleSize
' select sample
Dim n As Integer, x As Integer, z As Integer
n = 0
Do While n < iSampleSize
' pick one at random
x = 1 + Int(Rnd * iCount) ' avoid header row 0
'Debug.Print n, x
' update table if not previously chosen
If Len(myrows(x)) > 0 Then
ws.Range(myrows(x)).Offset(0, TABLE_COL - FILTER_COL) = "Sample"
myrows(x) = "" ' avoid repeat
n = n + 1
End If
z = z + 1 ' avoid endless loop
If z > LOOP_MAX Then
MsgBox "Max iterations in While Loop exceeded", vbCritical
Exit Sub
End If
Loop
MsgBox iSampleSize & " items selected from " & iCount, vbInformation, "Completed in " & Int(Timer - t0) & " secs"
End Sub
I have some calculations I need to do on a data-set. The calculations are simple (i.e input 1 x input 2 = output), but they take inputs recorded in two different excel workbooks by different people. Due to the different input sources the parameters between the two are sometimes in different orders or with slightly different names - the picture attached should show what I mean.
My plan was to take the relevant sheet of input book 1, and the relevant sheet of input book 2, and copy them into a single workbook where i hope to match the parameters with some sort of lookup/find macro, and perform the calculations automatically using a loop to work across the headers and down the rows
combined worksheet concept
Would really appreciate any help.
Even if you look less interested than me to solve this problem, I prepared the next code with the assumption that the array obtained from the the string "apple,banana,orange,grape" covers both sheets strange style head of columns naming.
I used "X1" for your first sheet name, "X2" for the second one and "Result" for the one matching values of the first two:
Sub MatchingLike_bis()
Dim arrNames As Variant, sh1 As Worksheet, sh2 As Worksheet, sRez As Worksheet
Dim lastR1 As Long, lastR2 As Long, arrRez As Variant, arr1 As Variant, arr2 As Variant
Dim i1 As Long, i2 As Long, El As Variant, k As Long, col1 As Long, col2 As Long
Dim strProbl1 As String, strProbl2 As String, colTot As Long, boolF As Boolean, i As Long
arrNames = Split("apple,banana,orange,grape,lemon", ",")
colTot = UBound(arrNames) + 2 'The array is zero based and A is excepted
Set sh1 = ThisWorkbook.Sheets("X1")
Set sh2 = ThisWorkbook.Sheets("X2")
Set sRez = ThisWorkbook.Sheets("Result")
lastR1 = sh1.Range("A" & sh1.Rows.count).End(xlUp).Row
lastR2 = sh2.Range("A" & sh2.Rows.count).End(xlUp).Row
arr1 = sh1.Range(sh1.Cells(1, 1), sh1.Cells(lastR1, colTot)).Value
arr2 = sh2.Range(sh2.Cells(1, 1), sh2.Cells(lastR1, colTot)).Value
'preliminary check if all fruits name has a corespondent in both necessary sheets:__________
strProbl1 = "": strProbl2 = ""
For Each El In arrNames
For i1 = 2 To colTot 'make checking in first sheet
If InStr(UCase(arr1(1, i1)), UCase(El)) > 0 Then
boolF = True: Exit For
End If
Next i1
If Not boolF Then strProbl1 = strProbl1 & El & vbCrLf
boolF = False
For i2 = 2 To colTot 'make checking in the second sheet
If InStr(UCase(arr2(1, i2)), UCase(El)) > 0 Then
boolF = True: Exit For
End If
Next i2
If Not boolF Then strProbl2 = strProbl2 & El & vbCrLf
boolF = False
Next
If strProbl1 <> "" Then MsgBox "In " & sh1.Name & " sheet, the next fruit names are" & _
" incorrect, or missing:" & vbCrLf & _
vbCrLf & strProbl1 & vbCrLf & "Please correct the spelling and run the application again!", _
vbInformation, "Wrong spelling in " & sh1.Name & " worksheet": sh1.Activate: Exit Sub
If strProbl2 <> "" Then MsgBox "In " & sh2.Name & " sheet, the next fruit names are" & _
" incorrect, or missing:" & vbCrLf & _
vbCrLf & strProbl2 & vbCrLf & "Please correct the spelling and run the application again!", _
vbInformation, "Wrong spelling in " & sh2.Name & " worksheet": sh2.Activate: Exit Sub
'_________________________________________________________________________________________________
ReDim arrRez(1 To UBound(arr1, 1), 1 To colTot) 'result array will have exactly the
'number of rows and columns as arr1
For i1 = 1 To UBound(arr1, 1)
If i1 = 1 Then
arrRez(i1, 1) = Empty
For i = 2 To colTot
arrRez(i1, i) = arr1(i1, i)
Next i
Else
For i2 = 1 To UBound(arr2, 1)
If arr1(i1, 1) = arr2(i2, 1) Then
arrRez(i1, 1) = arr1(i1, 1)
'find the right reference in the accepted keys array:
For Each El In arrNames
For k = 2 To colTot
If InStr(UCase(arr1(1, k)), UCase(El)) > 0 Then col1 = k
If InStr(UCase(arr2(1, k)), UCase(El)) > 0 Then col2 = k
Next k
If col1 > 0 And col2 > 0 Then
arrRez(i1, col1) = arr1(i1, col1) + arr2(i2, col2)
col1 = 0: col2 = 0
End If
Next
End If
Next i2
End If
Next i1
With sRez.Range(sRez.Range("A1"), sRez.Cells(lastR1, colTot))
.Value = arrRez
.EntireColumn.AutoFit
End With
End Sub
This version allows adding of a new fruit name in the string "apple,banana,orange,grape,lemon" (I already added lemon) and the code adapts itself to return as many columns as necessary. It makes a preliminary check and sends relevant messages for fruit names wrongly spelled in both input sheets. The code will completely run only if all the fruit names are matched in both input sheets...
I was trying to create a universal, error resistant VBA code that would count words in selected ranges as MS Word does. This below is the best I could do and I was hoping that somebody would have a look and let me know if I missed something or suggest any improvements. The code is quite fast and works with single cell, non-adjacent cells and whole columns, I need it to be as universal as possible. I'll be looking forward to feedback.
Option Explicit
Sub word_count()
Dim r() As Variant 'array
Dim c As Long 'total counter
Dim i As Long
Dim l As Long 'string lenght
Dim c_ch As Long 'character counter
Dim c_s As String 'string variable
Dim cell As range
Dim rng As range
If Selection Is Nothing Then
MsgBox "Sorry, you need to select a cell/range first", vbCritical
Exit Sub
ElseIf InStr(1, Selection.Address, ":", vbTextCompare) = 0 And InStr(1, Selection.Address, ",", vbTextCompare) = 0 Then 'for when only one cell is selected
word_count_f Selection.Value, c
MsgBox "Your selected cell '" & Replace(Selection.Address, "$", "") & "' in '" & Selection.Parent.Name & "' has " & c & " words."
Exit Sub
ElseIf InStr(1, Selection.Address, ",", vbTextCompare) > 0 Then 'when user selects more than one cell by clicking one by one -> address looks like ('A1,A2,A3') etc
Application.ScreenUpdating = False
Dim help() As Variant
ReDim help(1 To Selection.Cells.Count)
i = 1
For Each cell In Selection 'loading straigh to array wouldn't work, so I create a helper array
help(i) = cell.Value
i = i + 1
Next cell
r = help
Else 'load selection to array to improve speed
Application.ScreenUpdating = False
r = Selection.Value
End If
Dim item As Variant
For Each item In r
word_count_f item, c
Next item
MsgBox "Your selected range '" & Replace(Selection.Address, "$", "") & "' in '" & Selection.Parent.Name & "' has " & c & " words."
End Sub
Private Function word_count_f(ByVal item As Variant, ByRef c As Long)
Dim l As Long 'lenght variable
Dim c_s As String 'whole string variable
Dim c_ch As Long 'characted count variable
l = Len(item)
If l = 0 Then Exit Function
c_s = item
c_s = Trim(c_s)
Do While InStr(1, c_s, " ", vbTextCompare) > 0 'remove double spaces to improve accuracy
c_s = Replace(c_s, " ", " ")
Loop
If InStr(1, c_s, " ", vbTextCompare) = 0 And l > 0 Then 'if there was just one word in the cell
c = c + 1
ElseIf InStr(1, c_s, " ", vbTextCompare) > 0 Then 'else loop through string to count words
For c_ch = 1 To l 'loop through charactes of the string
If (Mid(c_s, c_ch, 1)) = " " Then
c = c + 1 'for each word
End If
Next c_ch
c = c + 1 'add one for the first word in cell
Else 'hopefully useless msgbox, but I wanted to be sure to inform the user correctly
MsgBox "Sorry, there was an error while processing one of the cells, the result might not be accurate", vbCritical
End If
End Function
You can achieve this in a similar way but with less code if you are interested to see?:
Sub word_count()
start_time = Timer
Dim r As Variant 'temp split array
Dim arr As Variant 'array
Dim c As Long 'total counter
If Selection Is Nothing Then
MsgBox "Sorry, you need to select a cell/range first", vbCritical
Exit Sub
Else
c = 0
For Each partial_selection In Split(Selection.Address, ",")
If Range(partial_selection).Cells.Count > 1 Then
arr = Range(partial_selection).Value
Else
Set arr = Range(partial_selection)
'single cell selected don't convert to array
End If
For Each temp_cell In arr
If Len(Trim(temp_cell)) > 0 Then
r = Split(temp_cell, " ")
For Each temp_word In r
If Len(Trim(temp_word)) > 0 Then
c = c + 1
'If the word is just a blank space don't count
End If
Next
'c = c + (UBound(r) - LBound(r) + 1)
'trimmed = Trim(temp_cell)
'c = c + 1 + (Len(trimmed) - Len(Replace(trimmed, " ", "")))
Else 'Blank cell
'Do nothing
End If
Next
Next
End If
Dim item As Variant
time_taken = Round(Timer - start_time, 3)
MsgBox "Your selected range '" & Replace(Selection.Address, "$", "") _
& "' in '" & Selection.Parent.Name & "' has " & c & " words." _
& vbNewLine & "Time Taken: " & time_taken & " secs"
Debug.Print c & " in "; time_taken; " secs"
End Sub
You could try this sort of approach? There may be the need to check for the next character to the space being another space, which would need some additions made. To detect word one as being the same as word one in the count. Also, transferring the range to an array would make it a touch faster.
Function Word_Count(rng As Excel.Range) As Long
Dim c As Excel.Range
Dim s As String
Dim l As Long
For Each c In rng.Cells
s = Trim(c.Value)
If s <> "" Then
If InStr(1, s, " ") > 0 Then
' Find number of spaces. You can use the ubound of split maybe here instead
l = l + (Len(s) - Len(Replace(s, " ", "")))
Else
End If
' Will always be 1 word
l = l + 1
End If
Next c
Word_Count = l
Set c = Nothing
End Function
I'm looking at a spreadsheet in excel
Name | Paperwork | Paperwork 1 | Paperwork 2
Joe | 1 | 1 | 1
Jane | 0 | 1 | 0
I'm trying to find the 0 in the spreadsheet, and output something like
There is an error in the Paperwork assigned to Jane for Paperwork 2
The VBA code I have is:
Private Sub CommandButton1_Click()
Dim i As Integer, j As Integer, Staff As String, Consumer As String, Error As String, CurCell As String
MsgBox "Starting the routine..."
For i = 2 To 3
If Cells(i, 2).Value = 0 Then
For j = 3 To 4
If Cells(i, j).Value = 1 Then
CurCell = i & ", " & j
Else
CurCell = i & ", " & j
MsgBox CurCell
End If
Next j
End If
Next i
End Sub
I'm trying to scan paperwork; its a column that says whether the paperwork is completed. Because Joe completed his paperwork, the algorithm moves past it. Jane however is missing paperwork 2. So when the algorithm arrives at position (Jane, Paperwork) it begins to look in row (paperwork)
For (Jane, Paperwork 1) the algorithm sees a 1, and moves to increment
For (Jane, Paperwork 2) the algorithm sees a 0, and what I want to do is display:
"Jane is missing" + Paperwork 2.
I would like to do something at that point like setting the Staff string variable to be = Cell(row i, j).value, and then outputting 'Staff' to somewhere on the spreadsheet, but I don't know the VBA syntax to be able to do so.
Sub ZeroError()
Dim rng As Range
Dim rowREF As Integer 'row reference
Dim colREF As Integer ' column reference
Dim eName As String 'name holder for employee
Dim wAssignment As String 'assignment holder e.g. Paperwork
Dim colLOCATION As Integer ' this is the column you want to put your results in
colLOCATION = 1 ' placing everying in column note that i add 6 in CELLS
rowREF = 1
colREF = 1
eName = ""
wAssignment = ""
Set rng = ActiveSheet.UsedRange
For Each cell In rng
If cell.Value = 0 Then
rowREF = cell.Row
colREF = cell.Column
eName = Cells(rowREF, 1)
wAssignment = Cells(1, colREF)
If (eName <> "" And wAssignment <> "") Then
If Cells(rowREF, colLOCATION + 6) <> "" Then
colLOCATION = colLOCATION + 1
Else
colLOCATION = 1
End If
Cells(rowREF, colLOCATION + 6) = eName & " " & "is missing" & " " & wAssignment
End If
End If
If cell.Value <> 0 Then
rowREF = cell.Row
colREF = cell.Column
eName = Cells(rowREF, 1)
wAssignment = Cells(1, colREF)
If (eName <> "" And wAssignment <> "") Then
If Cells(rowREF, colLOCATION + 6) <> "" Then
colLOCATION = colLOCATION + 1
Else
colLOCATION = 1
End If
Cells(rowREF, colLOCATION + 6) = eName & " " & "has completed" & " " & wAssignment
End If
End If
Debug.Print colLOCATION
Next
End Sub
Adjusted to your settings in the below answer - sorry I am not the most efficient coder but it should work out for you.
Using the .CurrentRegion as a starting point, you should be able to offset and loop through each of the numbered cells. I've put the results into an unused column to the right. this is the best that I could figure out from your narrative.
Sub lost_Paperwork()
Dim iStaffCol As Long, rng As Range
With ActiveSheet 'define this worksheet peoperly!
With .Cells(1, 1).CurrentRegion
iStaffCol = .Columns.Count + 2
For Each rng In .Offset(1, 1).Resize(.Rows.Count - 1, .Columns.Count - 1)
If rng.Value = 0 Then _
.Cells(Rows.Count, iStaffCol).End(xlUp).Offset(1, 0) = _
.Cells(rng.Row, 1).Value & ", missing " & .Cells(1, rng.Column).Value
Next rng
End With
.Cells(1, iStaffCol) = "Staff"
End With
End Sub
Your results should resemble the following.
I have an excel file written in this way:
187712 201 37 0.18
2525 580 149 0.25
136829 137 43 0.31
I need to export this file with same spaces, same formatting in a txt file. How can i do it? I've tryied Save As | Formatted Text (Space Delimited) (*.prn) but not working because i have an issue on the last column. Is there a macro? Thanks.
EDIT: i tryied a macro:
Sub TEST()
Dim c As Range, r As Range
Dim output As String
For Each r In Range("A1:L504").Rows
For Each c In r.Cells
output = output & " " & c.Value
Next c
output = output & vbNewLine
Next r
Open "D:\MyPath\text.txt" For Output As #1
Print #1, output
Close
End Sub
but the result is
187712 201 37 0.18
2525 580 149 0.25
136829 137 43 0.31
These values are only an example because there are about 504 columns!! Anyway the problem is that if in the first column there is a value shorter then the others it lost the formatting like the second row as you can see.
Your posted data shows fixed fields with field-widths of 8,7,7,4 (each field is a combination of characters and trailing blanks). These can be adjusted as necessary in the macro below. Also adjust the folder name to suit your needs:
Sub FixedField()
Dim fld(1 To 4) As Long
Dim V(1 To 4) As String
Dim N As Long, L As Long
Dim K As Long
fld(1) = 8
fld(2) = 7
fld(3) = 7
fld(4) = 4
N = Cells(Rows.Count, "A").End(xlUp).Row
Close #1
Open "c:\TestFolder\test.txt" For Output As #1
For L = 1 To N
outpt = ""
For K = 1 To 4
V(K) = Cells(L, K).Text
While Len(V(K)) <> fld(K)
V(K) = V(K) & " "
Wend
outpt = outpt & V(K)
Next K
MsgBox outpt
Print #1, outpt
Next L
Close #1
End Sub
It is also assumed that the data starts in column A.
I struggled with that also numerous times, the only way I found was with a VBA function I created (the tricky part is determining the "widest" column for plain-text layout). Fair warning: I didn't build a lot "smarts" into this, the output can be a little quirky.
Usage:
Select the cells you want formatted to plain-text, then run the macro (I have the macro assigned to a button, I use it all the time!). If the top row is center-aligned, then let's /assume/ it's a header. And watch for right-aligned columns, and output those right-aligned.
The marco will copy the desired output to the clip-board, then paste the result in Notepad (or similar) to do with as desired.
Example output (I threw in some headers)
CustId Views Selected Cost
187712 201 37 0.18
2525 580 149 0.25
136829 137 43 0.31
The code:
Sub FormatSelectionToPlainText()
' ---------------------------------------------------------------------------
' Author: Jay R. Ohman
' Ohman Automation Corp., http://www.OhmanCorp.com
' ** disclaimer and release: I am NOT an expert **
' ** programmer, use my coding at your own risk! **
' ---------------------------------------------------------------------------
Dim rFound As Range, RngCol1 As Integer, RngRow1 As Integer, ActCol As Integer, ActRow As Integer, x As Integer
Dim MaxCellLen() As Variant, CellAlignRight() As Variant, HdrLen() As Variant, xDbg As Boolean, xVal As Variant
Dim SepSpace As Integer, RetStr As String, RetLen As Integer, MsgStr As String, HasHdr As Boolean
Dim GeneralIsRightAlignedFactor As Single, TotalRows As Integer
Dim oClip As DataObject
xDbg = True ' output stuff to the immediate window?
GeneralIsRightAlignedFactor = 0.75 ' threshhold for deeming a column as right-aligned
Set oClip = New DataObject
MsgStr = "(looking for top row to be center aligned as header)"
If MsgBox("Are the cells to be copied selected?" & vbCrLf & MsgStr, vbYesNo + vbQuestion, "Auto-Fill Time Slots") = vbYes Then
If (Selection Is Nothing) Then
MsgBox "Nothing Selected."
Else
SepSpace = 2 ' number of spaces between columns
RetLen = 0
HasHdr = True
Set rFound = Selection
RngCol1 = rFound.Column
RngRow1 = rFound.Row
Debug.Print Selection.Columns.Count
ReDim Preserve MaxCellLen(Selection.Columns.Count) ' max cell length
ReDim Preserve CellAlignRight(Selection.Columns.Count) ' track the cell alignment
ReDim Preserve HdrLen(Selection.Columns.Count) ' header row max cell length
For ActCol = RngCol1 To RngCol1 + Selection.Columns.Count - 1
x = (ActCol - RngCol1 + 1)
' If xDbg Then Debug.Print Cells(RngRow1, ActCol).HorizontalAlignment
If (Cells(RngRow1, ActCol).HorizontalAlignment <> xlCenter) And (Cells(RngRow1, ActCol).Value <> "") Then HasHdr = False
HdrLen(x) = IIf(HasHdr, Len(Cells(RngRow1, ActCol).Value), 0)
MaxCellLen(x) = 0
CellAlignRight(x) = 0
Next
If xDbg Then Debug.Print "HasHdr: " & HasHdr
TotalRows = (RngRow1 + Selection.Rows.Count) - (RngRow1 + IIf(HasHdr, 1, 0))
For ActCol = RngCol1 To RngCol1 + Selection.Columns.Count - 1 ' go find the longest text in each column
x = (ActCol - RngCol1 + 1)
xVal = IIf(HasHdr, 1, 0)
For ActRow = RngRow1 + xVal To RngRow1 + Selection.Rows.Count - 1
' If xDbg Then Debug.Print Cells(ActRow, ActCol).HorizontalAlignment
xVal = Cells(ActRow, ActCol).Value
If (MaxCellLen(x) < Len(Cells(ActRow, ActCol).Value)) Then MaxCellLen(x) = Len(xVal)
If (Cells(ActRow, ActCol).HorizontalAlignment = xlRight) Or _
((Cells(ActRow, ActCol).HorizontalAlignment = xlGeneral) And (IsDate(xVal) Or IsNumeric(xVal))) Then _
CellAlignRight(x) = CellAlignRight(x) + 1
Next
If xDbg Then Debug.Print "Max Length for Column " & ActCol & ": " & MaxCellLen(x) & _
", CellAlignRight.Count: " & CellAlignRight(x) & "/" & TotalRows
RetLen = RetLen + MaxCellLen(x) + SepSpace
Next
RetLen = RetLen - SepSpace ' subtract that last separator space
If HasHdr Then
For ActCol = RngCol1 To RngCol1 + Selection.Columns.Count - 1
x = (ActCol - RngCol1 + 1)
If (HdrLen(x) > MaxCellLen(x)) Then MaxCellLen(x) = HdrLen(x)
Next
End If
RetStr = "" ' build the output text
For ActRow = RngRow1 To RngRow1 + Selection.Rows.Count - 1
For ActCol = RngCol1 To RngCol1 + Selection.Columns.Count - 1
x = (ActCol - RngCol1 + 1)
MsgStr = Cells(ActRow, ActCol).Value ' re-use string variable
' format for right-aligned
If (CellAlignRight(x) / TotalRows >= GeneralIsRightAlignedFactor) And (Not (HasHdr And (ActRow = 1))) Or (Cells(ActRow, ActCol).HorizontalAlignment = xlRight) Then ' aligned right
RetStr = RetStr & Space(MaxCellLen(x) - Len(MsgStr)) & MsgStr
ElseIf (Cells(ActRow, ActCol).HorizontalAlignment = xlCenter) Then
xVal = Fix((MaxCellLen(x) - Len(MsgStr)) / 2)
RetStr = RetStr & Space(xVal) & MsgStr & Space(MaxCellLen(x) - Len(MsgStr) - xVal)
Else
RetStr = RetStr & MsgStr & Space(MaxCellLen(x) - Len(MsgStr))
End If
If ((ActCol - RngCol1) + 1 < UBound(MaxCellLen)) Then RetStr = RetStr & Space(SepSpace)
Next
RetStr = RetStr & vbCrLf
Next
oClip.SetText RetStr
oClip.PutInClipboard
MsgBox ("The selection has been copied to clipboard." & vbCrLf & "Max line length: " & RetLen)
End If
Else
MsgBox ("Have a nice day. :)")
End If
End Sub