My rows do not have the same length and I need to avoid the "blanks" in between when I export to CSV.
For example, when I export this:
1 2 3 4 5
1 2
1 3 3 4
1 2 3 4 5
I get this:
1,2,3,4,5
1,2,,,
1,3,3,4,
1,2,3,4,5
And I need to remove the extra seperators from the empty cells between.
I am already running a macro to export as CSV, so it would be best if I could "delete" the empty cells in the beginning of this.
This small macro will:
avoid creating empty CSV records corresponding to empty Excel rows
avoid trailing commas
Option Explicit
Sub CSV_Makerr()
Dim r As Range
Dim sOut As String, k As Long, M As Long
Dim N As Long, nFirstRow As Long, nLastRow As Long
Dim MyFilePath As String, MyFileName As String
Dim fs, a, mm As Long
Dim separator As String
ActiveSheet.UsedRange
Set r = ActiveSheet.UsedRange
nLastRow = r.Rows.Count + r.Row - 1
nFirstRow = r.Row
separator = ","
MyFilePath = "C:\TestFolder\"
MyFileName = "whatever"
Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile(MyFilePath & MyFileName & ".csv", True)
For N = nFirstRow To nLastRow
k = Application.WorksheetFunction.CountA(Cells(N, 1).EntireRow)
sOut = ""
If k = 0 Then
Else
M = Cells(N, Columns.Count).End(xlToLeft).Column
For mm = 1 To M
sOut = sOut & Cells(N, mm).Text & separator
Next mm
sOut = Left(sOut, Len(sOut) - 1)
a.writeline (sOut)
End If
Next
a.Close
End Sub
I found it was pretty simple to solve, I just added a loop to check and delete if the last symbol in a line was a seperator
With Selection
StartRow = .Cells(1).Row
StartCol = .Cells(1).Column
EndRow = .Cells(.Cells.Count).Row
EndCol = .Cells(.Cells.Count).Column
End With
Else
With ActiveSheet.UsedRange
StartRow = .Cells(1).Row
StartCol = .Cells(1).Column
EndRow = .Cells(.Cells.Count).Row
EndCol = .Cells(.Cells.Count).Column
End With
End If
For RowNdx = StartRow To EndRow
WholeLine = ""
For ColNdx = StartCol To EndCol
If Cells(RowNdx, ColNdx).Value = "" Then
CellValue = ""
Else
CellValue = Cells(RowNdx, ColNdx).Value
End If
WholeLine = WholeLine & CellValue & Sep
Next ColNdx
WholeLine = Left(WholeLine, Len(WholeLine) - Len(Sep))
//I added this to delete the last seperator in a line before printing
Dim last As String
last = Right(WholeLine, 1)
Do Until last <> ","
WholeLine = Left(WholeLine, Len(WholeLine) - 1)
last = Right(WholeLine, 1)
Loop
Print #nFileNum, WholeLine
Next RowNdx
Related
I am trying to improve below VBA I found in this thread. Would it be possible to have this code in the form of Application.Dialogs(xlDialogSaveAs).Show(Arg2:=xlCSV) method, so I can choose where to save the CSV file?
Option Explicit
Sub CSV_Makerr()
Dim r As Range
Dim sOut As String, k As Long, M As Long
Dim N As Long, nFirstRow As Long, nLastRow As Long
Dim MyFilePath As String, MyFileName As String
Dim fs, a, mm As Long
Dim separator As String
ActiveSheet.UsedRange
Set r = ActiveSheet.UsedRange
nLastRow = r.Rows.Count + r.Row - 1
nFirstRow = r.Row
separator = ","
MyFilePath = "C:\TestFolder\"
MyFileName = "whatever"
Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile(MyFilePath & MyFileName & ".csv", True)
For N = nFirstRow To nLastRow
k = Application.WorksheetFunction.CountA(Cells(N, 1).EntireRow)
sOut = ""
If k = 0 Then
Else
M = Cells(N, Columns.Count).End(xlToLeft).Column
For mm = 1 To M
sOut = sOut & Cells(N, mm).Text & separator
Next mm
sOut = Left(sOut, Len(sOut) - 1)
a.writeline (sOut)
End If
Next
a.Close
End Sub
The idea is to remove the commas from the CSV or blank column that is persistently exist even after I delete it several times. Above code works, but without the liberty to choose the location path for different end users or PC. Kindly let me know if it's possible.
Something like this?
Sub CSV_Makerr()
Dim r As Range
Dim sOut As String, k As Long, M As Long
Dim N As Long, nFirstRow As Long, nLastRow As Long
Dim MyFilePath As String, MyFileName As String
Dim fs, a, mm As Long
Dim separator As String
ActiveSheet.UsedRange
Set r = ActiveSheet.UsedRange
nLastRow = r.Rows.Count + r.Row - 1
nFirstRow = r.Row
separator = ","
MyFilePath = Application.GetSaveAsFilename(fileFilter:="CSV Files (*.csv), *.csv")
If MyFilePath <> "" Then
Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile(MyFilePath, True)
For N = nFirstRow To nLastRow
k = Application.WorksheetFunction.CountA(Cells(N, 1).EntireRow)
sOut = ""
If k = 0 Then
Else
M = Cells(N, Columns.Count).End(xlToLeft).Column
For mm = 1 To M
sOut = sOut & Cells(N, mm).Text & separator
Next mm
sOut = Left(sOut, Len(sOut) - 1)
a.writeline (sOut)
End If
Next
a.Close
End If
End Sub
I try to find a vba solution for the following problem:
I have two columns and try to group column1 in a comma separate way to have less rows.
e.g.
example:
I tried this, and it worked - but It take too long (about 300.000 Rows). Is there any better solution that task?
*Its just one part of my macro
For Each r In fr
If st = "" Then
st = Application.WorksheetFunction.Clean(Trim(ws.Cells(r.row, "L").Value))
Else
If Not IsInArray(Split(st, ","), ws.Cells(r.row, "L").Value) Then
st = st & ", " & Application.WorksheetFunction.Clean(Trim(ws.Cells(r.row, "L").Value))
End If
End If
If usrCheck = True Then
If str = "" Then
str = Application.WorksheetFunction.Clean(Trim(ws.Cells(r.row, "A").Value))
Else
If Not IsInArray(Split(str, ","), ws.Cells(r.row, "A").Value) Then
str = str & ", " & Application.WorksheetFunction.Clean(Trim(ws.Cells(r.row, "A").Value))
End If
End If
End If
Next
Maybe using Dictionary would be fast. What about:
Sub Test()
Dim x As Long, lr As Long, arr As Variant
Dim dict1 As Object: Set dict1 = CreateObject("Scripting.Dictionary")
Dim dict2 As Object: Set dict2 = CreateObject("Scripting.Dictionary")
With Sheet1 'Change accordingly
'Return your last row from column A
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
'Get array and loop through it
arr = .Range("A2:B" & lr).Value
For x = LBound(arr) To UBound(arr)
dict1(arr(x, 2)) = arr(x, 2)
Next
'Loop through dictionary filling a second one
For Each Key In dict1.keys
For x = LBound(arr) To UBound(arr)
If arr(x, 2) = Key Then dict2(arr(x, 1)) = arr(x, 1)
Next x
.Range("F" & .Cells(.Rows.Count, 6).End(xlUp).Row + 1) = Key
.Range("G" & .Cells(.Rows.Count, 7).End(xlUp).Row + 1) = Join(dict2.Items, ", ")
dict2.RemoveAll
Next
End With
End Sub
This will get you all unique items from column A though, so if there can be duplicates and you want to keep them, this is not for you =)
Try also this, please. It works only in memory and on my computer takes less then 3 seconds for 300000 rows. The range must be filtered, like in your picture. If not, the filtering can also be easily automated.
Private Sub CondensData()
Dim sh As Worksheet, arrInit As Variant, arrIn As Variant, i As Long
Dim arrFinal() As Variant, lastRow As Long, Nr As Long, El As Variant
Dim strTemp As String, k As Long
Set sh = ActiveSheet
lastRow = sh.Cells(sh.Rows.count, "A").End(xlUp).Row
arrIn = sh.Range("B2:B" & lastRow + 1).Value
'Determine the number of the same accurrences:
For Each El In arrIn
i = i + 1
If i >= 2 Then
If arrIn(i, 1) <> arrIn(i - 1, 1) Then Nr = Nr + 1
End If
Next
ReDim arrFinal(Nr, 1)
arrInit = sh.Range("A2:B" & lastRow).Value
For i = 2 To UBound(arrInit, 1)
If i = 1 Then
strTemp = arrInit(1, 1)
Else
If arrInit(i, 2) = arrInit(i - 1, 2) Then
If strTemp = "" Then
strTemp = arrInit(i, 1)
Else
strTemp = strTemp & ", " & arrInit(i, 1)
End If
Else
arrFinal(k, 0) = arrInit(i - 1, 2)
arrFinal(k, 1) = strTemp
k = k + 1: strTemp = ""
End If
End If
Next i
sh.Range("C2:D" & lastRow).Clear
sh.Range("C2:D" & k - 1).Value = arrFinal
sh.Range("C:D").EntireColumn.AutoFit
MsgBox "Solved..."
End Sub
It will return the result in columns C:D
I am struggling with the following problem.
I want to do following operations on Input Col A and produce output in col B:
1.Remove Duplicates if any ( It was easy and completed )
2.Remove Leading and/or Trailing spaces from the string (It was easy as well and it's done )
3.COLLECT THE DIFFERENT TRANSLATIONS OF A WORD IN SAME CELL - AVOID DUPLICATES ( It's hard and I don't know how to proceed with this problem )
To understand this point have a look at input/output example.
Input:
A
absolution
absolution
absolutism
absolutism, absolute rule
absolutist
absolutist
absorb
absorb
absorb, bind
absorb, take up
absorb
absorb, imbibe, take up
absorb, sorb
absorb, take up
absorb, take up
absorb, imbibe
absorb
absorb
absorber
absorber
absorber
Output:
col B
absolution
absolutism, absolute rule
absolutist
absorb, bind, imbibe, take up, sorb
absorber
I tried with the following code but I am stuck on the third point/step
Option Explicit
Sub StrMac()
Dim wk As Worksheet
Dim i, j, l, m As Long
Dim strc, strd, fstrc, fstrd As String
Dim FinalRowC, FinalRowD As Long
Set wk = Sheet1
wk.Columns(1).Copy Destination:=wk.Columns(3)
wk.Columns(2).Copy Destination:=wk.Columns(4)
wk.Range("$C:$C").RemoveDuplicates Columns:=1, Header:=xlNo
wk.Range("$D:$D").RemoveDuplicates Columns:=1, Header:=xlNo
FinalRowC = wk.Range("C1048576").End(xlUp).Row
FinalRowD = wk.Range("D1048576").End(xlUp).Row
If FinalRowC >= FinalRowD Then
j = FinalRowC
Else
j = FinalRowD
End If
For i = 1 To j
If wk.Range("C" & i).Text <> "" Then
strc = wk.Range("C" & i).Text
strc = Replace(strc, Chr(160), "")
strc = Application.WorksheetFunction.Trim(strc)
wk.Range("C" & i).Value = strc
Else: End If
If wk.Range("D" & i).Text <> "" Then
strd = wk.Range("D" & i).Text
strd = Replace(strd, Chr(160), "")
strd = Application.WorksheetFunction.Trim(strd)
wk.Range("D" & i).Value = strd
Else: End If
Next i
Dim Cet, Det, Fet, Met, s As Variant
Dim newstr
Dim pos, cos As Long
s = 1
For i = 1 To j
If wk.Range("D" & i).Text <> "" Then
l = 2
strd = wk.Range("D" & i).Text
newstr = strd
For m = i + 1 To j
pos = 1100
cos = 2300
fstrd = wk.Range("D" & m).Text
cos = InStr(1, fstrd, ",")
pos = InStr(1, fstrd, strd, vbTextCompare)
If wk.Range("D" & m).Text <> "" And Len(fstrd) > Len(strd) And m <= j And cos <> 2300 And pos = 1 Then
l = 5
newstr = newstr & "," & fstrd
wk.Range("D" & m) = ""
Else: End If
Next m
wk.Range("E" & s) = newstr
s = s + 1
Else: End If
Next i
End Sub
Assuming your input is column A and you want the output in column B (as stated in your question), the following should work for you:
Sub tgr()
Dim ws As Worksheet
Dim rData As Range
Dim aData As Variant
Dim vData As Variant
Dim vWord As Variant
Dim aResults() As String
Dim sUnq As String
Dim i As Long
Set ws = ActiveWorkbook.Sheets("Sheet1")
Set rData = ws.Range("A1", ws.Cells(Rows.Count, "A").End(xlUp))
If rData.Cells.Count = 1 Then
'Only 1 cell in the range, check if it's no blank and output it's text
If Len(Trim(rData.Text)) > 0 Then ws.Range("B1").Value = WorksheetFunction.Trim(rData.Text)
Else
'Remove any extra spaces and sort the data
With rData
.Value = Evaluate("index(trim(" & .Address(external:=True) & "),)")
.Sort .Cells, xlAscending, Header:=xlNo
End With
aData = rData.Value 'Load all values in range to array
ReDim aResults(1 To rData.Cells.Count, 1 To 1) 'Ready the results array
For Each vData In aData
'Get only unique words
If InStr(1, vData, ",", vbTextCompare) = 0 Then
If InStr(1, "," & sUnq & ",", "," & vData, vbTextCompare) = 0 Then
sUnq = sUnq & "," & vData
If i > 0 Then aResults(i, 1) = Replace(aResults(i, 1), ",", ", ")
i = i + 1
aResults(i, 1) = vData
End If
Else
'Add unique different translations for the word
For Each vWord In Split(vData, ",")
If InStr(1, "," & aResults(i, 1) & ",", "," & Trim(vWord) & ",", vbTextCompare) = 0 Then
aResults(i, 1) = aResults(i, 1) & "," & Trim(vWord)
End If
Next vWord
End If
Next vData
End If
'Output results
If i > 0 Then ws.Range("B1").Resize(i).Value = aResults
End Sub
So I have one excel document that has partially filled character array ("|" shows excel column line):
Current Result ("_" is a space):
1111GGH80100022190
1112QQH80100023201
1113GGH80100045201
1114AAH80100025190
So my current code outputs this above result. The problem is that characters 1-5 and 21-24 get skipped over. In general if there is no column number accounted for I should print " " (Space).
Desired Result ("_" is a space):
_____1111GGH80100022____190
_____1112QQH80100023____201
_____1113GGH80100045____201
_____1114AAH80100025____190
Is there are column way to detect if I am missing a range in the header column? I currently use this and only select the data:
Private Sub WriteFile_Click()
Dim myFile As String, rng As Range, cellValue As Variant, I As Integer, j As Integer
myFile = "C:\Reformatted.txt"
Set rng = Selection
Open myFile For Output As #1
For I = 1 To rng.Rows.Count
For j = 1 To rng.Columns.Count
cellValue = cellValue + CStr(rng.Cells(I, j).Value)
cellValue = Replace(cellValue, "NULL", " ")
If j = rng.Columns.Count Then
Print #1, cellValue
End If
Next j
cellValue = ""
Next I
Close #1
Shell "C:\Windows\Notepad.exe C:\Reformatted.txt", 1
End Sub
-----------------------------After TMh885 Answer--------------------------------
So the code works perfectly EXCEPT if you have a header thats a single number '63'
So I am trying this:
Private Sub CommandButton1_Click()
Dim myFile As String, rng As Range, cellValue As Variant, I As Integer, j As Integer
myFile = "C:\Reformatted.txt"
Set rng = Selection
Open myFile For Output As #1
Dim strArr(1 To 63) As String, intBeg As Integer, intEnd As Integer, intCount As Integer
For I = 2 To rng.Rows.Count
For j = 1 To rng.Columns.Count
If Len(Cells) = 1 Then
cellValue = cellValue + " "
Else
intBeg = Val(Left(Cells(1, j).Value, InStr(1, Cells(1, j).Value, "-") - 1))
intEnd = Val(Right(Cells(1, j).Value, Len(Cells(1, j).Value) - InStr(1, Cells(1, j).Value, "-")))
intCount = 1
For t = intBeg To intEnd
strArr(t) = Mid(Cells(I, j).Value, intCount, 1)
intCount = intCount + 1
Next t
End If
Next j
For t = 1 To UBound(strArr)
If strArr(t) = "" Then strArr(t) = " "
cellValue = cellValue + strArr(t)
Next t
Erase strArr
Print #1, cellValue
cellValue = ""
Next I
Close #1
Shell "C:\Windows\Notepad.exe C:\Reformatted.txt", 1
End Sub
--------------------------------Error Snapshot----------------------------------
Input:
Here's your code with the extra functionality built in. It essentially just builds an array, adds each character to the correct location then adds it all together, replacing blanks with spaces. You'll have to change the maximum (in this example it's hard-coded at 27) but you could use the same logic as I used to get "intEnd" to find your maximum by looping over the heading column. Notes, this will compensate for out of order columns and I assumed that the Selection includes headers (hence starting at I = 2):
Private Sub WriteFile_Click()
Dim myFile As String, rng As Range, cellValue As Variant, I As Integer, j As Integer
myFile = "C:\Reformatted.txt"
Set rng = Selection
Open myFile For Output As #1
Dim strArr(1 To 27) As String, intBeg As Integer, intEnd As Integer, intCount As Integer
For I = 2 To rng.Rows.Count
For j = 1 To rng.Columns.Count
If InStr(1, CStr(Cells(1, j).Value), "-") = 0 Then
strArr(Val(Cells(1, j).Value)) = Cells(I, j).Value
Else
intBeg = Val(Left(Cells(1, j).Value, InStr(1, Cells(1, j).Value, "-") - 1))
intEnd = Val(Right(Cells(1, j).Value, Len(Cells(1, j).Value) - InStr(1, Cells(1, j).Value, "-")))
intCount = 1
For t = intBeg To intEnd
strArr(t) = Mid(Cells(I, j).Value, intCount, 1)
intCount = intCount + 1
Next t
End If
Next j
For t = 1 To UBound(strArr)
If strArr(t) = "" Then strArr(t) = " "
cellValue = cellValue + strArr(t)
Next t
Erase strArr
Print #1, cellValue
cellValue = ""
Next I
Close #1
Shell "C:\Windows\Notepad.exe C:\Reformatted.txt", 1
End Sub
I need to a dynamic way to concatinate some cells in a row with a delimiter (in this instance |) as the columns move about (per project) this has to be by header names (there might be from 2 to many) columns that need to be concatinated for a project
I am trying to use arrays as there can be as many as 4000,000 rows
I have been trying for hours and here is my effort I know it is very wrong but I am at a loss
Thank you
Sub CAT()
fCAT "ElementsFile", "ElementsFile", "D", Array("Age", "Gender Identity", "Ethnicity1", "Ethnicity1")
End Sub
Sub fCAT(sShtName As String, pbShtName As String, InsertCol As String, ar As Variant)
Dim myresult
Dim col1 As String, col2 As String, col3 As String, col4 As String
Dim aLR As Long, i As Long, j As Long, k As Long
'Totaly at a loss here
For i = LBound(ar) To UBound(ar)
Dim ari As Variant
Dim coli As String
Next i
Set wsS = ThisWorkbook.Sheets(sShtName)
Set wsPB = ThisWorkbook.Sheets(pbShtName)
With wsS
aLR = .Range("A" & .Rows.Count).End(xlUp).Row
For i = LBound(ar) To UBound(ar)
j = .Rows(1).Find(ar(i)).Column
ari = .Range(Cells(1, j), Cells(aLR, j)).Select
Next i
End With
'Totaly at a loss here
ReDim myresult(1 To aLR, 1 To aLR)
For k = 1 To aLR
For i = LBound(ar) To UBound(ar)
j = wsS.Rows(1).Find(ar(i)).Column
myresult(k, 1) = Cells(k, j) & "|" & Cells(k, j + 1) & "|" & Cells(k, j + 2) & "|" & Cells(k, j + 3)
Next i
Next k
wsT.Range("D1").Resize(aLR, 1) = myresult
End Sub
Here is what I finally came up with a bit of a mess maybe but it works
Sub concat()
Dim myresult, CN
Dim HN As Variant
Dim wsS As Worksheet, wsPB As Worksheet
Dim str As String
Dim LR As Long, i As Long, j As Long, k As Long
HN = Array("Age", "Gender Identity", "Ethnicity1", "Ethnicity2")
Set wsS = ThisWorkbook.Sheets("ElementsFile")
Set wsPB = ThisWorkbook.Sheets("ElementsFile")
wsPB.Columns(4).Insert
ReDim CN(0 To UBound(HN))
With wsS
LR = .Range("A" & .Rows.Count).End(xlUp).Row
'Get Array of column numbers coresponding to Header names
For i = 0 To UBound(HN)
j = wsS.Rows(1).Find(HN(i)).Column
CN(i) = j
Next i
End With
ReDim myresult(1 To LR, 1 To 1)
For i = 1 To LR
str = vbNullString
If Not (IsEmpty(Cells(i, CN(0))) And IsEmpty(Cells(i, CN(1)))) Then
For k = UBound(HN) To 0 Step -1
If k <> UBound(HN) Then
str = Cells(i, CN(k)) & "|" & str
Else: str = Cells(i, CN(k)) & str
End If
Next k
myresult(i, 1) = str
Else
myresult(i, 1) = vbNullString
End If
Next i
str = vbNullString
wsPB.Range("D1").Resize(LR, 1) = myresult
End Sub