how to remove cell value in the cell offset if a new value with the same data repeated after that? - excel

I want to make a loop in range to zero the offset cell if the cell data is repeated next as below:
Column A Column B
T10 5
T20 10
T10 15
You see I have T10 Repeated maybe after one or many rows, What I want is to use only the last value for T10 (Which is 15 in this example) and make 5 = 0
I use this code in vb.net
Private Sub AdjustTotalStoreWeightPerDiamater()
wb = Workbook Path ...
ws = Worksheet Name ...
lr = last row
Rng = Column Range
Cel = Column Range
i = integer
With ws
lr = .Range("A" & .Rows.Count).End(Excel.XlDirection.xlUp).Row
Rng = ws.Range("A2", "A" & lr)
End With
For i = 2 To lr
For Each Cel In Rng.Cells
If Cel.Value = Cel.Offset(-1, 0).Value Then
Cel.Offset(0, 3).Value = 0
End If
Next
Next
End Sub
But the code does not sense that T10 = T10 to make the change.
Appreciate your support.

I used OleDb to get the data from Excel as a DataTable. Note that I used Extended Properties=""Excel 12.0;HDR=YES;""" at the end of the connection string so we would get the column headers.
Private Function GetExcelData() As System.Data.DataTable
Dim dt As New System.Data.DataTable
Dim ConStr = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\xxxx\Documents\Code\TestCode3\TestCode3\bin\Debug\TestCode1.xlsx;Extended Properties=""Excel 12.0;HDR=YES;"""
Dim SheetName = "Sheet1$"
Using cn = New OleDb.OleDbConnection(ConStr)
Using cmd As New OleDbCommand($"Select * From [{SheetName}];", cn)
cn.Open()
Using dr As OleDbDataReader = cmd.ExecuteReader
dt.Load(dr)
End Using
End Using
End Using
Return dt
End Function
To save the data back to the same excel sheet I used office automation.
Private Sub SaveDataTableToExcel(dt As System.Data.DataTable, RowToStartAt As Integer)
Dim FileToOpen = "C:\Users\xxxx\Documents\Code\TestCode3\TestCode3\bin\Debug\TestCode1.xlsx"
Dim xl As New Excel.Application
Dim wb = DirectCast(xl.Workbooks.Open(FileToOpen), Workbook)
Dim ws = DirectCast(wb.Worksheets(1), Excel.Worksheet)
'Add Headers
For ColumnIndex = 1 To dt.Columns.Count
ws.Cells(RowToStartAt, ColumnIndex) = dt.Columns(ColumnIndex - 1).Caption
Next
'Write data to Excel
For dtRow = 0 To dt.Rows.Count - 1
RowToStartAt += 1
Dim xlColumnIndex = 1
For Each dtcolumn As DataColumn In dt.Columns
ws.Cells(RowToStartAt, xlColumnIndex) = dt(dtRow)(dtcolumn)
xlColumnIndex += 1
Next
Next
wb.Save()
wb.Close()
xl.Quit()
End Sub
The magic happens in happens in the Linq query where we group by Column A and select the Max from Column B. Then it is a simple loop through the returned enumerable to fill a new DataTable that is passed to Excel for saving.
Private Sub OPCode()
Dim dt = GetExcelData()
Dim e = From row In dt.AsEnumerable
Group By TValue = row.Field(Of String)("Column A")
Into Ts = Group, Max(row.Field(Of Double)("Column B"))
Select TValue, Max
Dim Newdt = dt.Clone 'Copies structure but not data
For Each row In e
Newdt.Rows.Add({row.TValue, row.Max})
Next
DataGridView1.DataSource = Newdt 'Just checking if we got the expected data
Dim WhereToAdd = dt.Rows.Count + 3 'Two rows down from original data
SaveDataTableToExcel(Newdt, WhereToAdd)
MessageBox.Show("Done")
End Sub

Related

Loop through and copy paste values without repetition if conditions are met

Im trying to create a table that pulls data from my raw data if certain conditions are met. The code I currently have does not seem to be working.
Public Sub insert_rows()
Dim datasheet As Worksheet
Dim datasheet2 As Worksheet
Dim r As Long
Dim tableA As ListObject
Set tableA = Worksheets(Sheet7).ListObject(Preventable)
Set datasheet = Worksheets(Sheet7)
Set datasheet2 = Worksheets("Data")
With datasheet2
nr = Cells(Rows.Count, 1).End(x1up).Row
For r = 1 To nr
If Cells(r, 17) = "Y" Then
Cells(r, 16).Copy Destination:=Sheets("Sheet7").Range("B4")
End If
Next
End With
End Sub
Basically I have several worksheets and need to pull data from one of them to add to this table in another worksheet. My condition is if the Column in the raw data worksheet contains "Y", then pull cell values into the table of the other worksheet. An image below is an example of the data I want to copy and paste over:
As you can see, they are string values separated by "," and can contain duplicates.
I only want to add just the unique entries into the new table; with no repetition of cells. Anyway I could modify this code to suit those conditions?
You could try something like this:
Public Sub insert_rows()
Dim datasheet As Worksheet
Dim datasheet2 As Worksheet
Dim r As Long, i As Long, nr As Long
Dim tableStartingRow As Long, currenttableitem As Long
Dim stringvalues As Variant
Dim stringseparator As String
Dim valueexists As Boolean
tableStartingRow = 4
stringseparator = ","
Set datasheet = Worksheets("Sheet7")
Set datasheet2 = Worksheets("Data")
With datasheet
currenttableitem = .Cells(.Rows.Count, 2).End(xlUp).Row
End With
With datasheet2
nr = .Cells(.Rows.Count, 16).End(xlUp).Row
For r = 1 To nr
If .Cells(r, 17) = "Y" Then
If InStr(.Cells(r, 16), stringseparator) > 0 Then 'If value contains comma
stringvalues = Split(.Cells(r, 16), stringseparator)
For i = LBound(stringvalues) To UBound(stringvalues)
valueexists = False 'Reset boolean
For x = tableStartingRow To currenttableitem
If datasheet.Range("B" & x).Value = Trim(stringvalues(i)) Then
valueexists = True
Exit For
End If
Next x
If Not valueexists Then
currenttableitem = currenttableitem + 1
datasheet.Range("B" & currenttableitem).Value = Trim(stringvalues(i))
End If
Next i
Else
valueexists = False 'Reset boolean
For x = tableStartingRow To currenttableitem
If datasheet.Range("B" & x).Value = .Cells(r, 16).Value Then
valueexists = True
Exit For
End If
Next x
If Not valueexists Then
currenttableitem = currenttableitem + 1
datasheet.Range("B" & currenttableitem).Value = .Cells(r, 16).Value
End If
End If
End If
Next
End With
End Sub
This code will check each value of the cells and will split the contents by ",". Then compare with the content of the table to see if this value is already in there. In case it is not, it will be added, otherwise omitted.
Also, I notice the use of the Cells inside of a With statement. That was making a reference to the active worksheet. To make reference to the item in the With statement, you need to use .Cells
I hope this will help.

Need help copy/pasting in Excel VBA from one workbook to another

I need to find out how to write some basic code that will take each cell's value (which will be an ID number) from a selected range, then match it to a cell in a master workbook, copy said cell's entire row, then insert it into the original document in place of the ID number. Here's the kicker: certain ID numbers may match with several items, and all items that have that number must be inserted back into the document. Here's an example:
Master Document Workbook
A B C D A B C D
1 a ab ac 2
2 b bc bd 3
2 b be bf
3 c cd de
I would select the cells containing 2 and 3 in the Workbook, which after running the code would give me this:
Workbook
A B C D
2 b bc bd
2 b be bf
3 c cd de
Here's what I have going on so far but it's a total mess. The only thing it's managed to successfully do is store the selected range in the Workbook I want to paste to. It won't compile past that because I don't understand much of the syntax in VBA:
Sub NewTest()
Dim rng As Range
Dim FirstRow As Range
Dim CurrentCol As String
Dim FirstRowVal As Integer
Dim CurrentColVal As Variant
Dim rngOffset As Range
CurrentCol = "Blah"
Set FirstRow = Application.InputBox("Select the row containing your first raw material", Type:=8)
FirstRowVal = FirstRow.Row
Set rng = (Application.InputBox("Select the cells containing your IC numbers", "Obtain Materials", Type:=8))
Set rngOffset = rng.Offset(0, FirstRowVal)
CurrentColVal = rng.Column
Call CopyPaste
End Sub
Sub CopyPaste()
Dim Blah As Range
Set x = Workbooks.Open("Workbook Path")
Workbooks.Open("Workbook Path").Activate
Set y = Workbooks.Open("Master Path")
Workbooks.Open("Master Path").Activate
With x
For Each Cell In rng
x.Find(rng.Cell.Value).Select
If Selection.Offset(0, -1) = Selection Then
Selection.EntireRow.Copy
Selection = Selection.Offset(0, -1)
Else
Selection.EntireRow.Copy
Blah = Selection
End If
Workbooks.Open("Workbook Path").Activate
Sheets("Formula Sheet").Select
Blah.Insert (rng.Cell)
End
Sheets("sheetname").Cells.Select
Range("A1").PasteSpecial
'Sheets("sheetname").PasteSpecial
.Close
End With
With x
.Close
End With
End Sub
Would very much appreciate anyone who could help point me in the right direction. Thanks.
I'll bite, you can use the output array to populate any range on any worksheet.
Sub FindAndMatch()
Dim arrMatchFrom() As Variant, arrMatchTo() As Variant, arrOutput() As Variant
Dim i As Integer, j As Integer, counter As Integer
counter = 0
arrMatchFrom = Range("A2:D6")
arrMatchTo = Range("G2:G3")
For i = LBound(arrMatchTo, 1) To UBound(arrMatchTo, 1)
For j = LBound(arrMatchFrom, 1) To UBound(arrMatchFrom, 1)
If arrMatchTo(i, 1) = arrMatchFrom(j, 1) Then
counter = counter + 1
ReDim Preserve arrOutput(4, counter)
arrOutput(1, counter) = arrMatchTo(i, 1)
arrOutput(2, counter) = arrMatchFrom(j, 2)
arrOutput(3, counter) = arrMatchFrom(j, 3)
arrOutput(4, counter) = arrMatchFrom(j, 4)
End If
Next
Next
For i = 1 To counter
For j = 1 To 4
Debug.Print arrOutput(j, i)
Cells(9 + i, j) = arrOutput(j, i)
Next
Next
End Sub

Delete specific rows using range function

I want to delete all rows in excel sheet if specific column value starts with 1.
For example, if range of A1:A having values starts with 1 then I want to delete all those rows using excel vba.
How to get it?
Dim c As Range
Dim SrchRng
Set SrchRng = Sheets("Output").UsedRange
Do
Set c = SrchRng.Find("For Men", LookIn:=xlValues)
If Not c Is Nothing Then c.EntireRow.Delete
Loop While Not c Is Nothing
Here's the required code with comments on how it works. Feed the worksheet and column number to the sub and call it e.g. Delete Rows 2, Sheets("myWorksheet"):
Sub DeleteRows(columnNumber as Integer, ws as WorkSheet)
Dim x as long, lastRow as Long
' get the last used row
lastRow = ws.cells(1000000, columnNumber).end(xlUp).Row
'loop backwards from the last row and delete applicable rows
For x = lastRow to 1 Step -1
' if the cell starts with a number...
If IsNumeric(Left(ws.Cells(x, columnNumber), 1) Then
'Delete it the row if it's equaal to 1
If Left(ws.Cells(x, columnNumber), 1) = 1 Then ws.Rows(x &":"& x).Delete
End If
Next x
End Sub
Dim Value As String
Dim CellName As String
Dim RowNumber As Long
Do While Value <> ""
CellName = "A" + RowNumber
Value = ActiveSheet.Cells(GetRowNumber(CellName), GetColumnNumber(CellName)).Value
If Mid(Value, 1, 1) = "2" Then
ActiveSheet.Range("A" & RowNumber).EntireRow.Delete
End If
RowNumber = RowNumber + 1
Loop
Private Function GetColumnNumber(ByVal CellName As String) As Long
For L = 1 To 26
If Left(CellName, 1) = Chr(L + 64) Then
GetColumnNumber = L
Exit For
End If
Next
End Function
Private Function GetRowNumber(ByVal CellName As String) As Long
GetRowNumber = CLng(Mid(CellName, 2))
End Function
You may be pushing the bounds of what is reasonable to do in Excel vba.
Consider importing the Excel file into Microsoft Access.
Then, you can write 2 Delete Queries and they will run uber fast:
DELETE FROM MyTable WHERE col1 like '2*'
DELETE FROM MyTable WHERE col2 LIKE '*for men*' OR col3 LIKE '*for men*'
After deleting those records, you can export the data to a new Excel file.
Also, you can write an Access Macro to import the Excel File, run the Delete Queries, and Export the data back to Excel.
And you can do all of this without writing a line of VBA Code.
You can try:
Sub delete()
tamano = Range("J2") ' Value into J2
ifrom = 7 ' where you want to delete
'Borramos las celdas
'Delete column A , B and C
For i = ifrom To tamano
Range("A" & i).Value = ""
Range("B" & i).Value = ""
Range("C" & i).Value = ""
Next i
End Sub

Copy data from one table and Clear and update new data into another table in another sheet in excel 2010

I have a VBA macro which is currently copying data from Setup sheet and updating into the respective tables into Read_Only sheet for the first time. But when I click second time, it is adding the data into the respective tables in Read_Only sheet.
Now what I want is, if I click second time, it should first clear the existing data from that respective table in Read_Only sheet and then update the new data into that table. (For example: In 1st table, there were 10 rows of data, now when I click 2nd time I have only 8 rows of data, then macro should clear data existing 10 rows of data and update this new 8 rows of data and then delete the 2 empty two rows. This should be Dynamic, since number of rows may vary every time while updating new data)
Here is the existing code:
Sub copyData()
Dim wsSet As Worksheet
Dim wsRead As Worksheet
Dim rngSearch As Range
Dim lastRow As Integer
Dim i As Integer
Dim wRow As Integer
Dim strCat As String
Dim catRow As Integer
Set wsSet = ActiveWorkbook.Worksheets("Budget_Setup")
Set wsRead = ActiveWorkbook.Worksheets("WBS_Overview_Read_only")
Set rngSearch = wsRead.Range("A12:A1000") 'range in READ to search for category
lastRow = wsSet.Range("B16").End(xlDown).Row 'last row of data in SET
Application.ScreenUpdating = False
For i = 17 To lastRow
strCat = Left(wsSet.Range("b" & i).Value, 3) 'current category in SET
catRow = rngSearch.Find(strCat).Row 'row of match in READ
If wsRead.Range("a" & catRow + 1).Value = "" Then 'find the correct row to copy into
wRow = catRow + 1
Else
wRow = wsRead.Range("a" & catRow).End(xlDown).Row + 1
If wsRead.Range("e" & wRow).Value <> "" Then
wsRead.Range("a" & wRow).EntireRow.Insert
End If
End If
wsSet.Range("b" & i & ":f" & i).Copy
wsRead.Range("a" & wRow).PasteSpecial
Application.CutCopyMode = False
Next i
Application.ScreenUpdating = True
Set wsRead = Nothing
Set wsSet = Nothing
End Sub
This code will first delete all the existing data in each of the sections on the Read_Only sheet; then, with one modification, your code can be run as is.
Add this line of code immediately after Application.ScreenUpdating = False
' Erase all data in the Read Only Sheet
Set currentData = wsRead.Columns(4).Find("Subject")
Do
wsRead.Range(currentData.Offset(2, 0), _
currentData.Offset(2, 0).End(xlDown).Offset(-1, 0)).EntireRow.Delete
Set currentData = wsRead.Columns(4).FindNext(currentData)
Loop Until Not currentData Is Nothing And currentData.Row = 12
This code uses the "Subject" and the "Budgeted Cost" cells to delete the existing data between it.
Next, add the following line of code immediately after wRow = catRow + 1
wsRead.Rows(wRow).EntireRow.Insert
this will add the first blank row of data to a given section. Your existing code will then insert the new data into the blank row
See if this works for you. I added one line to your code:
For i = 17 To lastRow
strCat = Left(wsSet.Range("b" & i).Value, 3) 'current category in SET
catRow = rngSearch.Find(strCat).Row 'row of match in READ
If wsRead.Range("a" & catRow + 1).Value = "" Then 'find the correct row to copy into
wRow = catRow + 1
wsRead.Rows(wRow).EntireRow.Insert 'I added this line
Else
wRow = wsRead.Range("a" & catRow).End(xlDown).Row + 1 'end of data
If wsRead.Range("e" & wRow).Value <> "" Then
Now, run this code before running yours.
Sub deletePhases()
' delete phases in Setup from ReadOnly
Dim r As Range, Col As Collection
Dim x As Long, l As Long
With Budget_Setup
Set r = .Range("b17", .Cells(.Rows.Count, 2).End(xlUp))
End With
If r.Row < 17 Then Exit Sub 'no data
Set Col = New Collection 'build unique list
On Error Resume Next
For x = 1 To r.Rows.Count
Col.Add Left(r(x).Value, 3), Left(r(x).Value, 3)
Next x
With ReadOnly
For x = 1 To Col.Count
l = .Columns(1).Find(Col(x)).Offset(1).Row '1 below heading
Do Until .Cells(l, 1) = "" 'end of phase data
.Rows(l).Delete
Loop
Next x
End With
End Sub
I'm not sure how you're defining your Phase.71, Phase.72, etc, ranges, but with the information we have, this might work for you.
Sub clearAll()
Dim r As Range, vArr, v
vArr = Array("Phase.71", "Phase.72", "Phase.73", "Phase.74", "Phase.75")
For Each v In vArr
Set r = ReadOnly.Range(v)
Set r = r.Offset(2).Resize(r.Rows.Count - 4)
r.ClearContents
Next v
End Sub

Normalizing Excel Grid Intersection data into a flat list

I am trying to get Excel data, which was mapped using a grid/matrix mapping into a de-normalized for so that i can enter the data into a database.
How do you copy data in a grid from one excel sheet to the other as follow illustrated below.
I was trying something like this... but as you can see, i am far off!
Sub NormaliseList(mySelection As Range)
Dim cell As Range
Dim i As Long
i = 1
For Each cell In mySelection
If cell <> "" Then
Sheets(2).Range("A" & i).Value = cell(cell.Row, 1).Value
Sheets(2).Range("B" & i).Value = cell.Value
Sheets(2).Range("C" & i).Value = cell(1, cell.Column).Value
i = i + 1
Next cell
End Sub
For Reference. I Updated my code..
Simply add the code, assign macro shortcut to the function
Select the range that contains the intersection data (not the row and column data)
Run macro (Beware, sheet 2 will have data added in normalised form)
If there are multiple headings that are needed i figured i would consolidate into one column then perform a "text to columns" after processing.
Sub NormaliseList()
' to run - assign macro shortcut to sub - Select Intersection data (not row and column headings and run)
Dim Rowname, ColumnName, IntValue As String
Dim x, cntr As Integer
Dim test As Boolean
cntr = 0
For x = 1 To Selection.Count
If Selection(x).Value <> "" Then
cntr = cntr + 1
Rowname = ActiveSheet.Cells(Selection.Cells(x).Row, Selection.Column - 1)
ColumnName = ActiveSheet.Cells(Selection.Row - 1, Selection.Cells(x).Column)
IntValue = Selection(x).Value
test = addrecord(Rowname, ColumnName, IntValue, cntr)
End If
Next x
End Sub
Function addrecord(vA, vB, vC As String, rec As Integer) As Boolean
'Make sure that you have a worksheet called "Sheet2"
Sheets("Sheet2").Cells(rec, 1) = vA
Sheets("Sheet2").Cells(rec, 2) = vB
Sheets("Sheet2").Cells(rec, 3) = vC
End Function
I've got two posts, with usable code and downloadable workbook, on doing this in Excel/VBA on my blog:
http://yoursumbuddy.com/data-normalizer
http://yoursumbuddy.com/data-normalizer-the-sql/
Here's the code:
'Arguments
'List: The range to be normalized.
'RepeatingColsCount: The number of columns, starting with the leftmost,
' whose headings remain the same.
'NormalizedColHeader: The column header for the rolled-up category.
'DataColHeader: The column header for the normalized data.
'NewWorkbook: Put the sheet with the data in a new workbook?
'
'NOTE: The data must be in a contiguous range and the
'rows that will be repeated must be to the left,
'with the rows to be normalized to the right.
Sub NormalizeList(List As Excel.Range, RepeatingColsCount As Long, _
NormalizedColHeader As String, DataColHeader As String, _
Optional NewWorkbook As Boolean = False)
Dim FirstNormalizingCol As Long, NormalizingColsCount As Long
Dim ColsToRepeat As Excel.Range, ColsToNormalize As Excel.Range
Dim NormalizedRowsCount As Long
Dim RepeatingList() As String
Dim NormalizedList() As Variant
Dim ListIndex As Long, i As Long, j As Long
Dim wbSource As Excel.Workbook, wbTarget As Excel.Workbook
Dim wsTarget As Excel.Worksheet
With List
'If the normalized list won't fit, you must quit.
If .Rows.Count * (.Columns.Count - RepeatingColsCount) > .Parent.Rows.Count Then
MsgBox "The normalized list will be too many rows.", _
vbExclamation + vbOKOnly, "Sorry"
Exit Sub
End If
'You have the range to be normalized and the count of leftmost rows to be repeated.
'This section uses those arguments to set the two ranges to parse
'and the two corresponding arrays to fill
FirstNormalizingCol = RepeatingColsCount + 1
NormalizingColsCount = .Columns.Count - RepeatingColsCount
Set ColsToRepeat = .Cells(1).Resize(.Rows.Count, RepeatingColsCount)
Set ColsToNormalize = .Cells(1, FirstNormalizingCol).Resize(.Rows.Count, NormalizingColsCount)
NormalizedRowsCount = ColsToNormalize.Columns.Count * .Rows.Count
ReDim RepeatingList(1 To NormalizedRowsCount, 1 To RepeatingColsCount)
ReDim NormalizedList(1 To NormalizedRowsCount, 1 To 2)
End With
'Fill in every i elements of the repeating array with the repeating row labels.
For i = 1 To NormalizedRowsCount Step NormalizingColsCount
ListIndex = ListIndex + 1
For j = 1 To RepeatingColsCount
RepeatingList(i, j) = List.Cells(ListIndex, j).Value2
Next j
Next i
'We stepped over most rows above, so fill in other repeating array elements.
For i = 1 To NormalizedRowsCount
For j = 1 To RepeatingColsCount
If RepeatingList(i, j) = "" Then
RepeatingList(i, j) = RepeatingList(i - 1, j)
End If
Next j
Next i
'Fill in each element of the first dimension of the normalizing array
'with the former column header (which is now another row label) and the data.
With ColsToNormalize
For i = 1 To .Rows.Count
For j = 1 To .Columns.Count
NormalizedList(((i - 1) * NormalizingColsCount) + j, 1) = .Cells(1, j)
NormalizedList(((i - 1) * NormalizingColsCount) + j, 2) = .Cells(i, j)
Next j
Next i
End With
'Put the normal data in the same workbook, or a new one.
If NewWorkbook Then
Set wbTarget = Workbooks.Add
Set wsTarget = wbTarget.Worksheets(1)
Else
Set wbSource = List.Parent.Parent
With wbSource.Worksheets
Set wsTarget = .Add(after:=.Item(.Count))
End With
End If
With wsTarget
'Put the data from the two arrays in the new worksheet.
.Range("A1").Resize(NormalizedRowsCount, RepeatingColsCount) = RepeatingList
.Cells(1, FirstNormalizingCol).Resize(NormalizedRowsCount, 2) = NormalizedList
'At this point there will be repeated header rows, so delete all but one.
.Range("1:" & NormalizingColsCount - 1).EntireRow.Delete
'Add the headers for the new label column and the data column.
.Cells(1, FirstNormalizingCol).Value = NormalizedColHeader
.Cells(1, FirstNormalizingCol + 1).Value = DataColHeader
End With
End Sub
You’d call it like this:
Sub TestIt()
NormalizeList ActiveSheet.UsedRange, 1, "Name", "Count", False
End Sub

Resources