VBA Writes Extra Lines When Impossible To Do So - excel

I was wokring on the piece of code below, and had it functioning correctly. Somehow a line has changed and meant now it fails to function
I have a table of tags and weightings like so:
Tag | Weight
---------------
Sport | 1
Music | 1
And then another table of users, with tag + weight
User | Tag | Weight
The cell(j, "B") contains the username, as does the cell(2,"C") in the other worksheet
I am using the following code:
Sub swipeleft()
LastRowUser = Worksheets(13).Range("B65536").End(xlUp).Row
LastRowInput = Worksheets(14).Range("F65536").End(xlUp).Row
LastRowUser = LastRowUser + 1
newcount = 1
For j = 2 To LastRowUser
For k = 9 To LastRowInput
If Worksheets(14).Cells(k, "F") = Worksheets(13).Cells(j, "C") And Worksheets(13).Cells(j, "B") = Worksheets(14).Cells(2, "C") Then
Worksheets(13).Cells(j, "D") = Worksheets(13).Cells(j, "D") - Worksheets(14).Cells(k, "G")
ElseIf Not Worksheets(13).Cells(j, "B") = Worksheets(14).Cells(2, "C") Then
Worksheets(13).Cells(newcount + LastRowUser, "C") = Worksheets(14).Cells(k, "F")
Worksheets(13).Cells(newcount + LastRowUser, "D") = Worksheets(14).Cells(k, "G") * (-1)
Worksheets(13).Cells(newcount + LastRowUser, "B") = Worksheets(14).Cells(2, "C")
newcount = newcount + 1
End If
Next k
Next j
End Sub
This adds the rows when data is not present, but for some reason after the first run it keeps adding exponentially more rows, even though the second else condition is not met?
UPDATED FROM COMMENTS BELOW
Here is the user input page (Worksheet 14):
Here is the user database page (Worksheet 13):
On the user database page I would like it to add the two rows that dont exist (Music, Dance) and add the Sports tag weighting (-1) from the input page to the current value in user database page

Is this what you want?
Code:
Dim AR
Dim nWeight As Long
Dim wsI As Worksheet, wsO As Worksheet
Dim LRowWsI As Long, LRowWsO As Long, NewRowWsO As Long
Sub swipeleft()
Dim i As Long, j As Long
Set wsI = ThisWorkbook.Sheets(14)
Set wsO = ThisWorkbook.Sheets(13)
LRowWsI = wsI.Range("F" & wsI.Rows.Count).End(xlUp).Row
LRowWsO = wsO.Range("B" & wsI.Rows.Count).End(xlUp).Row
NewRowWsO = LRowWsO + 1
AR = wsI.Range("F9:G" & LRowWsI).Value
With wsO
For i = LBound(AR) To UBound(AR)
For j = 2 To LRowWsO
If RecordExists(wsI.Range("C2").Value, AR(i, 1)) Then
.Range("D" & j).Value = AR(i, 2)
Else
.Range("B" & NewRowWsO).Value = wsI.Range("C2").Value
.Range("C" & NewRowWsO).Value = AR(i, 1)
.Range("D" & NewRowWsO).Value = AR(i, 2)
NewRowWsO = NewRowWsO + 1
End If
Next j
Next i
End With
End Sub
Function RecordExists(sUser As Variant, sTag As Variant) As Boolean
Dim a As Long
With wsO
For a = 2 To LRowWsO
If .Range("B" & a).Value = sUser And .Range("C" & a).Value = sTag Then
RecordExists = True
Exit For
End If
Next
End With
End Function
Screenshot:

Related

Copy value and paste under matching column near respective row

I have a column with certain values which are also the headers for some columns. I want to check where the column values match and paste the value from the first column into the column with the same column name. I have around 1200 values in the first column. I want to loop through those values and paste the matching values in the corresponding row.
[![Data][1]][1]
Here is my sheet with my data that I want to work on. How I want my final sheet to look like is as follows:
Weeks | W1 | W2 | W3 | W4 | W5 | W6
W1 W1
W3 W3
Any help for the same would be highly appreciated.
Sub Weeks()
Application.ScreenUpdating = False
Dim i As Long, j As Long, Mas As Worksheet
Set Mas = Sheets("Master Sheet")
For i = 5 To 1200
If Mas.Range("B" & i) <> "" Then
If Mas.Range("AO" & i) = "Missing week" Then
Mas.Range("AV" & i) = ""
Mas.Range("AW" & i) = ""
Mas.Range("AX" & i) = ""
Mas.Range("AY" & i) = ""
Mas.Range("AZ" & i) = ""
Mas.Range("BA" & i) = ""
Else
For j = 5 To 1200
If Mas.Range("AO" & i) = "W1" Then
Mas.Range("AV" & j) = "W1"
GoTo Nexti
ElseIf Mas.Range("AO" & i) = "W2" Then
Mas.Range("AW" & j) = "W2"
GoTo Nexti
ElseIf Mas.Range("AO" & i) = "W3" Then
Mas.Range("AX" & j) = "W3"
GoTo Nexti
ElseIf Mas.Range("AO" & i) = "W4" Then
Mas.Range("AY" & j) = "W4"
GoTo Nexti
ElseIf Mas.Range("AO" & i) = "W5" Then
Mas.Range("AZ" & j) = "W5"
GoTo Nexti
ElseIf Mas.Range("AO" & i) = "W6" Then
Mas.Range("BA" & j) = "W6"
GoTo Nexti
End If
Next j
End If
End If
Nexti:
Next i
End Sub
This is the code I tried so far but it does not show any output.
This is how you use the dictionary to achieve your goal:
Option Explicit
Sub Weeks()
Dim Mas As Worksheet: Set Mas = ThisWorkbook.Sheets("Master Sheet")
With Mas
'Calculate thelast row
Dim i As Long
Dim LastRow As Long: LastRow .Cells(.Rows.Count, "AO").End(xlUp).Row
'insert your data into an array
Dim arr As Variant: arr = .Range("AO5:BA" & LastRow).Value
'Generate a dictionary with the headers
'this needs the library Microsoft Scripting Runtime under Tools->References
Dim Headers As Dictionary: Set Headers = LoadHeaders(.Range("AO4:BA4").Value)
'Now loop through the array
For i = 1 To UBound(arr)
If Headers.Exists(arr(i, 1)) Then arr(i, Headers(arr(i, 1))) = arr(i, 1)
arr(i, 1) = vbNullString
Next i
.Range("AO5:BA" & LastRow).Value = arr
End With
End Sub
Private Function LoadHeaders(arr As Variant) As Dictionary
Set LoadHeaders = New Dictionary
Dim i As Long
For i = 1 To UBound(arr, 2)
LoadHeaders.Add arr(1, i), i
Next i
End Function
You won't even need the Application.ScreenUpdating because it does only one operation in Excel, will take a second or two to end this procedure.
Place this code in a module and link it to a button on the sheet.
'data
Cells.Clear
wks = Array("W1", "W2", "W3", "W4", "W5", "W6", "Missing week")
For i = 1 To 30
Cells(i, 2) = wks(Int(Rnd * 7))
Next i
'code
Set weeks = [b:b]
For Each wk In weeks
If Len(wk) = 2 Then Cells(wk.Row, Right(wk, 1) + 2) = wk
Next wk
weeks contains the column that has the list of column headings. The For..Each statement loops through each entry in this list.
Then it checks each entries string length. If it is length 2, it assumes the entry is valid (i.e of the form 'Wx' with x between 1 and 6), and then uses the inbuilt Right function to find the value of x, and then adds the appropriate entry into the appropriate column.

Grouping two columns to shrink row count by comparing | code optimization

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

How to get Excel VBA to Create and Fill in Data if Needed

1) Write a statement in excel that will insert rows and fill missing data for days missing any hour. Hours in “DATE_HR” should go from 00-23 (24 hour time).
And
2) For hours that are listed, under “DATE_HR” (DD-MMM-YYYY-HH), that are missing “0” (which is NDG in “Class”) “1-4”, “GR”, and/or “SB” in “CLASSIFICATION”, for any given hour, write a statement that will insert and fill missing rows in all hours that has the missing “CLASSIFICATION”, “Class”, “DATE_HR”, AND “Total” (which missing “TOTAL” row values should be zero since there was no entry for the missing data).
Below is an example of what the program needs to do. The left is the missing data table (before) and the right is the corrected table (after), Yellow is 1 and blue is 2
-Here is my progress up to now:
I have written pseudo code for the issue and have started writing at in excel VBA. Here is the pseudo code:
SR = Selected_row
RA = Row_above
C = Classification
DT = Date & Time
IR=Insert_row
# = Any number 1-4
Start on seleted row
Loop statement:
= IF(SRC = ”GR” AND RAC = 4 AND SRDT== RADT, SR,
OR(SRC = ”SB” AND RAC = “GR” AND SRDT== RADT, SR,
OR(SRC = 0 AND RAC = “SB” AND SRDT== RADT -1day/+22hour, SR,
OR(SRC = # AND RAC = SRC -1 AND SRDT == RADT, SR,
OR(SRC = 0 AND RADT = -1day of SRC/23hour, SRC = “0” AND SRDT= RADT +1day/00hour,IR AND
IF(RAC = ”SB” AND RADT = 23hour, SRC = “0” AND SRDT= RADT +1day/00hour,
OR (RAC = ”SB”, SRC = “0” AND SRDT= RADT +1hour,
OR (RAC = ”GR”, SRC = “SB” AND SRDT= RADT,
OR (RAC = 4, SRC = “GR” AND SRDT= RADT,
OR(RAC = # AND SRC = RAC +1 AND SRDT == RADT, SR *here # = 0-3
)))))))))))))
Move onto next row below previous row
IF(SR=””, END program, continue)
Here is the excel VBA code: (the colors are just it see if it’s doing what it should)
Sub IF_Loop()
Dim i As Long
For i = 2 To 155
If (Range("B" & i).Value = "GR" And Range("B" & i - 1).Value = 4 And Range("C" & i).Value = Range("C" & i - 1).Value) Or _
(Range("B" & i).Value = "SB" And Range("B" & i - 1).Value = "GR" And Range("C" & i).Value = Range("C" & i - 1).Value) Or _
(Range("B" & i).Value = "4" And Range("B" & i - 1).Value = "3" And Range("C" & i).Value = Range("C" & i - 1).Value) Or _
(Range("B" & i).Value = "3" And Range("B" & i - 1).Value = "2" And Range("C" & i).Value = Range("C" & i - 1).Value) Or _
(Range("B" & i).Value = "2" And Range("B" & i - 1).Value = "1" And Range("C" & i).Value = Range("C" & i - 1).Value) Or _
(Range("B" & i).Value = "1" And Range("B" & i - 1).Value = "00" And Range("C" & i).Value = Range("C" & i - 1).Value) Then
Rows(i & ":" & i).Interior.Color = 9359529
Else
'insert row and correct data
Rows(i & ":" & i).EntireRow.Insert shift:=x1Down And _
Rows(i & ":" & i)
End If
Next i
I’m not sure how to write the remaining code. How do you properly write the remaining lines so the code will execute the tasks that are needed?
I would do this differently.
You need to know your starting and ending dates, and you also need to have a list of ALL of the Classifications and the associated Classes. (I hard coded both within the macro, but you can use other schemes).
From that you can create a table with all of the classes and all of the hours for all of the dates.
Once you've done that, you can look up to see if the Totals are available for the classification/date combination, and either write that in, or, if not present, a zero.
I used a class object which contains the information. Each of these objects has a collection (dictionary) of all the date_hr | total combinations available for that classification, and also a method to return the class for a given classification.
Working with VBA arrays is orders of magnitude faster than multiple read/writes to/from worksheets.
I hopefully have commented the code enough so you can understand what is going on.
For an excellent discussion of class objects, see the late Chip Pearsons Introduction to Classes. If this link dies, you'll need to do a web search. There is also an article there on reading/writing arrays to/from worksheet ranges which you will find useful.
Read the comments, especially at the beginning of each module, carefully so as to properly set things up, otherwise, it won't run.
It does assume that your data has a header row, and starts in A1.
The results are placed on the same worksheet, but it should be obvious how to change that.
Class Module
'**Rename this module: cClass**
Option Explicit
Private pClass As String
Private pClassification As String
Private pDate_HR As Date
Private pDate_HRs As Dictionary
Public Property Get class() As String
Select Case Me.Classification
Case "1"
class = "Freshman"
Case "2"
class = "Sophomore"
Case "3"
class = "Junior"
Case "4"
class = "Senior"
Case "GR"
class = "Graduate"
Case "SB"
class = "Second Bachelor"
Case "0"
class = "NDG"
Case Else
class = "N/A"
End Select
End Property
Public Property Get Classification() As String
Classification = pClassification
End Property
Public Property Let Classification(Value As String)
pClassification = Value
End Property
Public Property Get Date_HR() As Date
Date_HR = pDate_HR
End Property
Public Property Let Date_HR(Value As Date)
pDate_HR = Value
End Property
Public Property Get Date_HRs() As Dictionary
Set Date_HRs = pDate_HRs
End Property
Public Function addDate_HRsItem(dtHR As Date, toTAL As Long)
Date_HRs.Add Key:=dtHR, Item:=toTAL
End Function
Private Sub Class_Initialize()
Set pDate_HRs = New Dictionary
pDate_HRs.CompareMode = TextCompare
End Sub
Regular Module
Option Explicit
'set reference to microsoft scripting runtime
Sub fillData()
Dim wsSrc As Worksheet, wsRes As Worksheet, rRes As Range
Dim vSrc As Variant, vRes As Variant
Dim I As Long, J As Long
Dim dD As Dictionary, cc As cClass
Dim sKey As String, sDTkey As Date
'set source and results worksheets, range
Set wsSrc = Worksheets("sheet1")
Set wsRes = Worksheets("sheet1")
Set rRes = wsRes.Cells(1, 7)
'read source data into vba array
With wsSrc
vSrc = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp)).Resize(columnsize:=4)
End With
'Process the known data
'collect it into a dictionary for fast lookups
Set dD = New Dictionary
dD.CompareMode = TextCompare
For I = 2 To UBound(vSrc, 1)
Set cc = New cClass
With cc
.Classification = vSrc(I, 1)
.Date_HR = convDTHR(vSrc(I, 3))
.addDate_HRsItem .Date_HR, CLng(vSrc(I, 4))
sKey = .class
If Not dD.Exists(sKey) Then
dD.Add sKey, cc
Else
dD(sKey).addDate_HRsItem .Date_HR, CLng(vSrc(I, 4))
End If
End With
Next I
'Create Results Array
'Unclear from your question how many dates you want, so will
' just do Mar 4
Const dtStart As Date = #3/4/2019#
Const dtEnd As Date = #3/5/2019#
'code the list of all Classifications
Dim arrClass
arrClass = Array(0, 1, 2, 3, 4, "GR", "SB")
ReDim vRes(0 To (dtEnd - dtStart + 1) * 24 * (UBound(arrClass) + 1), 1 To 4)
'write the column Headers into a results array
For J = 1 To 4
vRes(0, J) = vSrc(1, J)
Next J
'fill in other columns
For I = 1 To UBound(vRes, 1) Step UBound(arrClass) + 1
For J = 0 To UBound(arrClass)
vRes(I + J, 1) = arrClass(J) 'Classification
vRes(I + J, 2) = convCLASS(arrClass(J)) 'class
vRes(I + J, 3) = Format(dtStart + Int((I + J - 1) / (UBound(arrClass) + 1)) / 24, "dd-mmm-yyyy hh") 'The Date_hr
sKey = vRes(I + J, 2) 'key into dictionary
If dD.Exists(sKey) Then
sDTkey = convDTHR(vRes(I + J, 3)) 'key into collection of date/totals within the dictionary item
If dD(sKey).Date_HRs.Exists(sDTkey) Then
vRes(I + J, 4) = dD(sKey).Date_HRs(sDTkey)
Else
vRes(I + J, 4) = 0
End If
Else
vRes(I + J, 4) = 0
End If
Next J
Next I
Set rRes = rRes.Resize(UBound(vRes, 1) + 1, UBound(vRes, 2))
With rRes
.EntireColumn.Clear
.Value = vRes
.Columns(1).HorizontalAlignment = xlCenter
.EntireColumn.AutoFit
End With
End Sub
Private Function convDTHR(strDTHR) As Date
convDTHR = CDate(Left(strDTHR, 11)) + Right(strDTHR, 2) / 24
End Function
Private Function convCLASS(strClassification) As String
Dim cc As cClass
Set cc = New cClass
With cc
.Classification = strClassification
convCLASS = .class
End With
End Function

Restructuring massive excel document

I have a task to restructure a two column excel sheet and expand it. Here is a picture to show what needs to be done, the data on the left of the green column is the original data and the data on the right is how it should look, but its only done for the first entry, I need to replicate it for all 10,000 rows of data.
To explain more indepth, each CRD it needs to be expanded to 160 rows, and go from 1978->2018 while listing the quarters out for each year. What is the best approach? Is it possible to write a macro to solve this ?
The following expects Sheet1 ans Sheet2 to be the names. And goes for 158 quarters.
Option Explicit
Sub doFromThru()
' clear contents
Sheets("Sheet2").Select
Cells.Select
Selection.ClearContents
Range("A1").Select
Cells(1, "A") = "CRD"
Cells(1, "B") = "Year"
Cells(1, "C") = "Quarter"
Cells(1, "D") = "QuarterNumerical"
Cells(1, "E") = "Disclosure"
Dim nOutRow As Integer
nOutRow = 1
' step thru all the rows on the input sheet
Dim nInRow As Long, maxInRow As Long, nInCRD As String, nInDisc As String
maxInRow = Sheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row
For nInRow = 2 To maxInRow
nInCRD = Sheets("Sheet1").Cells(nInRow, "A")
nInDisc = Sheets("Sheet1").Cells(nInRow, "L")
' create the new rows on Sheet2
Dim dFrom As String, nQtr As Integer
dFrom = DateValue("Oct 1978") ' starting from here
For nQtr = 1 To 158
nOutRow = nOutRow + 1
Sheets("Sheet2").Cells(nOutRow, "A") = nInCRD
Sheets("Sheet2").Cells(nOutRow, "B") = Format$(dFrom, "yyyy")
Sheets("Sheet2").Cells(nOutRow, "C") = Format$(dFrom, "Q")
Sheets("Sheet2").Cells(nOutRow, "D") = nQtr
Sheets("Sheet2").Cells(nOutRow, "E") = nInDisc
dFrom = DateAdd("Q", 1, dFrom)
Next nQtr
Next nInRow
End Sub
Add some diagnostics to tell you more. After nOutRow = nOutRow + 1
Sheets("Sheet2").Cells(1, "G") = nInRow
Sheets("Sheet2").Cells(1, "H") = nOutRow
Sheets("Sheet2").Cells(1, "I") = nQtr
Sheets("Sheet2").Cells(1, "J") = nInDisc
Untested.
Code assumes you want to start from Q4 of the year 1978 and will loop
for 159 quarters after 1978 Q4. (If necessary, you can change this by changing the value of TOTAL_QUARTERS and START_QUARTER in the code)
You will need to change "Sheet1" in the code to whatever the name of your sheet is.
Code tries to overwrite the contents of columns CH to CL on said sheet. So you might want to save a copy of your workbook before running.
Code:
Option Explicit
Sub ExpandRows()
Const START_YEAR as long = 1978
Const START_QUARTER as long = 4
Const TOTAL_QUARTERS as long = 160
With thisworkbook.worksheets("Sheet1")
Dim lastRow as long
lastRow = .cells(.rows.count, "A").row
Dim inputCRD() as variant
inputCRD = .range("A2:A" & lastRow).value2
Dim inputDisclosure() as variant
inputDisclosure = .range("L2:L" & lastRow).value2
Dim yearOffset as long
Dim quarterIndex as long
Dim numericalQuarterIndex as long
Dim totalRowCount as long
totalRowCount = (lastRow - 1) * TOTAL_QUARTERS ' -1 to skip first row
Dim outputArray() as variant
Redim outputArray(1 to totalRowCount, 1 to 5)
Dim readIndex as long
Dim writeIndex as long
For readIndex = lbound(inputCRD,1) to ubound (inputCRD,1)
quarterIndex = START_QUARTER
For numericalQuarterIndex = 1 to TOTAL_QUARTERS
writeIndex = writeIndex + 1
outputArray(writeIndex, 1) = inputCRD(readIndex, 1)
outputArray(writeIndex, 2) = START_YEAR + yearOffset
outputArray(writeIndex, 3) = quarterIndex
outputArray(writeIndex, 4) = numericalQuarterIndex
outputArray(writeIndex, 5) = inputDisclosure(readIndex, 1)
If quarterIndex < 4 then
quarterIndex = quarterIndex + 1
Else
yearOffset = yearOffset + 1
quarterIndex = 1
End if
Next numericalQuarterIndex
Next readIndex
.range("CH2").resize(ubound(outputArray,1), ubound(outputArray,2)).value2 = outputArray
End with
End sub

add 2 new columns with values present in row headers

H,
i have an inventory sheet in this format.(provided image below, did not know how to add table over here). The company name and category are not in separate columns. instead the are listed in the product column. i want to add 2 additional columns, one for company and one for category, and add details to every row correspondingly.
original format
desired format
what would be the simplest way to do this?
This should do what you want, it assumes your data starts in column A:
Sub ChangeFormat()
Dim CompanyName As String, Catgory As String, LR As Long
LR = Range("A" & Rows.Count).End(xlUp).Row
For X = 2 To LR
If X > LR Then Exit For
If Left(UCase(Range("A" & X).text), 8) = "COMPANY:" Then
CompanyName = Trim(Right(Range("A" & X).text, Len(Range("A" & X).text) - 8))
Rows(X).Delete
LR = LR - 1
End If
If Left(UCase(Range("A" & X).text), 9) = "CATEGORY:" Then
Category = Trim(Right(Range("A" & X).text, Len(Range("A" & X).text) - 9))
Rows(X).Delete
LR = LR - 1
End If
Range("E" & X).Formula = CompanyName
Range("F" & X).Formula = Category
Next
End Sub
It scans through and when it finds one of the headings it assigns that value to the variable then deletes the row, if it finds neither then it assumes it is data and posts the Company and Category to columns E and F (I think Catagory is spelled wrong but I went with your spelling of it).
Make sure you back up your data before running this as it will modify what you have.
Here's a macro to do the task. You can change the name of the sheet as indicated in the code.
Sub InventoryReformat()
Dim ar
Dim i As Long
Dim wRow As Long
Dim sTxt As String, sCompany As String, sCategory As String
Dim wsS As Worksheet, wsD As Worksheet
Set wsS = Sheets("SOURCE_DATA") 'Change as required
Set wsD = Sheets("DESTINATION") 'Change as required
ar = wsS.Range("A1").CurrentRegion.Value 'Change start cell as required
wRow = 1 'Change first destination row as required
With wsD
.Cells(wRow, 1).Resize(1, 6) = Split("PRODUCT|COST PRICE|SALE PRICE|TAX|CATEGORY|COMPANY", "|")
wRow = wRow + 1
For i = 2 To UBound(ar, 1)
sTxt = ar(i, 1)
If InStr(1, sTxt, "Company") > 0 Then
sCompany = Trim(Split(sTxt, ":")(1))
Else
If InStr(1, sTxt, "Category") > 0 Then
sCategory = Trim(Split(sTxt, ":")(1))
Else
.Cells(wRow, 1) = ar(i, 1)
.Cells(wRow, 2) = ar(i, 2)
.Cells(wRow, 3) = ar(i, 3)
.Cells(wRow, 4) = ar(i, 4)
.Cells(wRow, 5) = sCategory
.Cells(wRow, 6) = sCompany
wRow = wRow + 1
End If
End If
Next i
.Cells(wRow - 1, 1).CurrentRegion.Columns.AutoFit
End With
End Sub
.
Hope this will help you.

Resources