Trace Dependencies for user defined range and list Source Cell/Dependency Cell in separate sheet - excel

I have a range of cells (user defined) that I want vba to tell me which cell(s) these are linked to.
Every source cell can be linked to 1 or multiple cells.
I have the code so far that it
prompts the user for the range
checks that only 1 row is selected.
counts the amount of cells in the range.
creates a separate sheet to list the dependencies.
I am struggling to have the each source cell listed horizontally and 2 rows below the dependency cell(s).
Option Explicit
Sub ListDependents()
Dim rng As Range
Dim r As Range
Dim cell As Range
Dim n As Long, i As Long
Application.ScreenUpdating = False
'Use InputBox to prompt user for range.
'Test for cancel and a single-cell selection.
Set rng = Application.InputBox( _
Title:="Please select a range", _
Prompt:="Select range", _
Type:=8)
On Error GoTo 0
'Test for cancel.
If rng Is Nothing Then Exit Sub
'Test for single-cell selection.
'Remove comment character if single-cell selection is okay.
If rng.Rows.Count > 1 Then
MsgBox "You’ve selected more than 1 row. Please select contiguous cells per row only."
End If
'rng.Select to confirm selection
MsgBox rng.Address
'count cells to be reviewed for dependencies
For Each cell In rng.Areas
n = n + cell.Cells.Count
Next cell
Sheets.Add().Name = "Dependents"
'add first cell of range in B1, second in C1 etc until end of range
'then add first dependent of first range cell in B3, second in C3 etc
If n > "0" Then
i = 1 + i
Sheets("Depentent Test").Cells(2, i) =
End Sub
Source Sheet
Destination Sheet

Try this. I would suggest replacing my variable names with more useful ones. I haven't included a check that a cell has any dependents which is advisable as otherwise it will probably error.
Sub ListDependents()
Dim rng As Range
Dim r As Range
Dim cell As Range
Dim n As Long, i As Long, j As Long
Application.ScreenUpdating = False
'Use InputBox to prompt user for range.
'Test for cancel and a single-cell selection.
Set rng = Application.InputBox( _
Title:="Please select a range", _
Prompt:="Select range", _
Type:=8)
On Error GoTo 0
'Test for cancel.
If rng Is Nothing Then Exit Sub
'Test for single-cell selection.
'Remove comment character if single-cell selection is okay.
If rng.Rows.Count > 1 Then
MsgBox "You’ve selected more than 1 row. Please select contiguous cells per row only."
End If
'rng.Select to confirm selection
MsgBox rng.Address
Sheets.Add().Name = "Dependents"
'add first cell of range in B1, second in C1 etc until end of range
'then add first dependent of first range cell in B3, second in C3 etc
Dim ra As Range, r1 As Range, r2 As Range
j = 2
For Each ra In rng.Areas
For Each r1 In ra
Cells(1, j) = r1.Address
i = 3
For Each r2 In r1.Dependents
Cells(i, j) = r2.Address
i = i + 1
Next r2
j = j + 1
Next r1
Next ra
End Sub

I found a solution to add the headers above each dependency. I moved the rows down by one to have space for a header and then used offset to match the respective header.
j = 2
For Each ra In rng.Areas
For Each r1 In ra
Cells(2, j) = r1.Address
Cells(1, j) = r1.OffSet(-1, 0).Value
i = 4
For Each r2 In r1.Dependents
Cells(i, j) = r2.Address
Cells(i - 1, j) = r2.OffSet(-1, 0).Value
i = i + 2
Next r2
j = j + 1
Next r1
Next ra

Related

VBA - Loop through and copy/paste value on range based on different cell value

I have been struggling with this code. I want to loop through Column E beginning with E5, on the Sheet titled "pivot of proposal" (which is a pivot table); and every time it finds a cell with the value of "check" I want it to copy/paste value of cells A & B of the corresponding row to the sheet titled Check Payments in E & F, moving down a row each time but beginning on row 4. I tried to piece together other bits of code but it is not doing what I need it to.
Sub Loop_Check_Payments()
Dim c As Range
Dim IRow As Long, lastrow As Long, krow as long
Dim copyrow As Integer
Dim rSource As Range
Dim DataOrigin As Worksheet, DataDest As Worksheet, DataDestACH As Worksheet
On Error GoTo Whoa
'~~> Sheet Where "L" needs to be checked
Set DataOrigin = ThisWorkbook.Sheets("Pivot of proposal")
'~~> Output sheet
Set DataDest = ThisWorkbook.Sheets("CHECK PAYMENTS")
Set DataDestACH = ThisWorkbook.Sheets("ACH_WIRE PAYMENTS CASH POOLER")
Application.ScreenUpdating = False
'~~> Set you input range
Set rSource = Range("Payment_Method")
'~~> Search for the cell which has "L" and then copy it across to sheet1
For Each c In rSource
If c.Value = "Check" Then
DataDest.Cells(4 + IRow, 5) = DataOrigin.Cells(c.Row, 1)
DataDest.Cells(4 + IRow, 6) = DataOrigin.Cells(c.Row, 2)
IRow = IRow + 1
Else
DataDestACH.Cells(4 + kRow, 7) = DataOrigin.Cells(c.Row, 1)
DataDestACH.Cells(4 + kRow, 8) = DataOrigin.Cells(c.Row, 2)
kRow = kRow + 1
End If
Next c
Whoa:
MsgBox Err.Description
End Sub
Instead of trying to Copy/paste - you can do something like this (as PeterT alluded to in comments)
this will put values from columns A&B (ordinal 1 & 2) of the SOURCE to the same row/column in the destination:
If c.Value = "Check" Then
DataDest.Cells(c.Row, 1) = DataOrigin.Cells(c.Row, 1)
DataDest.Cells(c.Row, 2) = DataOrigin.Cells(c.Row, 2)
End If

Find previous cell address (to the left) in active row with different value than active cell value

I,ve tried to find a VBA solution for finding the previous cell (located on the same row) with different value than the selected cell has. So if the selected cell is for example [N6] (as in my picture) then my search range should be ("A6:N6") from which I need to find the last cell with a different cell value (which would be cell [L6] in my picture because it's the previous cell with a different value than cell [N6]. The search should start from the end (N6,M6,L6...) backwards until the first match is found (first different cell value). When the first match is found then select it. I have hundreds of columns, so my picture is just to show the principle. I execute my vba code with Private Sub Worksheet_SelectionChange(ByVal Target As Range) so when the user selects a cell with the mouse. I get the desired cell with {=ADDRESS(6;MATCH(2;1/(A6:O6<>"D")))} but I would need a VBA solution for my problem. My current VBA solution takes me to cell [I6] instead of [L6] and I can't figure out how to edit my code to find the correct cell ([L6] in my example picture).
Dim rngSel As String, rngStart As String
Dim rngActiveStart As Range
rngSel = ActiveCell.Address(0, 0)
rngStart = Cells(ActiveCell.Row, 1).Address(0, 0)
Set rngActiveStart = Range(rngStart & ":" & rngSel)
Dim c
For Each c In rngActiveStart.Cells
If c <> Target.Value And c.Offset(0, 1) = Target.Value Then
c.Select
MsgBox "Previous different cell: " & c.Address(0, 0)
Exit For
End If
Next
Using selection_Change
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim r As Long, col As Long, x
Dim v As String
r = Target.Row
v = Target.Value
Application.EnableEvents = False
For x = Target.Column To 1 Step -1
With Me
If .Cells(r, x) <> v Then
.Cells(r, x).Select
Exit For
End If
End With
Next x
Application.EnableEvents = True
End Sub
You need a For i = max To min Step -1 loop to loop backwards/left:
Public Sub MoveLeftUntilChange()
Dim SelRange As Range 'remember the selected range 'N6
Set SelRange = Selection
Dim iCol As Long
For iCol = SelRange.Column To 1 Step -1 'move columns leftwards
With SelRange.Parent.Cells(SelRange.Row, iCol) 'this is the current row/column to test againts the remembered range N6
If .Value <> SelRange.Value Then 'if change found select and exit
.Select
Exit For
End If
End With
Next iCol
End Sub

Using VBA in Excel to share cell vales between sheets to populate data in cells

Updated Question
I have a VBA script attached to sheet 1 that uses the B5:B50 cell values to populate the adjacent column with pre-defined text. If I want to use this script in another sheet, but still use the B5:B50 cell values of the previous sheet. How to I do that?
For Example:
In sheet 1, If I enter the value of 2 in the cell B5, it will populate D5 and E5 with the text value attached to CONST TXT. I want to do the same thing in sheet 2, but instead of the user entering the value again into B5 of sheet 2, it just gets the value of B5 from the previous sheet and then populate D5 and E5.
Sheet 2 B values will need to update as soon as the B values are updated in Sheet 1.
Private Sub Worksheet_Change(ByVal Target As Range)
Const NUM_COLS As Long = 5
Const TXT = "• Course Name:" & vbNewLine & _
"• No. Of Slides Affected:" & vbNewLine & _
"• No. of Activities Affected:"
Dim rng As Range, i As Long, v
If Target.CountLarge <> 1 Then Exit Sub
If Not Intersect(Target, Me.Range("B5:B50")) Is Nothing Then
Set rng = Target.Offset(0, 2).Resize(1, NUM_COLS) 'range to check
v = Target.Value
If IsNumeric(v) And v >= 1 And v <= NUM_COLS Then
For i = 1 To rng.Cells.Count
With rng.Cells(i)
If i <= v Then
'Populate if not already populated
If .Value = "" Then .Value = TXT
Else
'Clear any existing value
.Value = ""
End If
End With
Next i
Else
rng.Value = "" 'clear any existing content
End If
End If
End Sub
As I understand you, you want something like an equivalent of offset which returns a range on a different sheet. There are a couple of options.
You can use Range.AddressLocal, which returns the address of Range without any worksheet or workbook qualifiers, and then apply this to the other worksheet:
'returns a cell 1 to the right of rng, but on Sheet2
Worksheets("Sheet2").Range(rng.Offset(0, 1).AddressLocal)
Or you can get the Row and Column properties of your range and use them in Cells in the other worksheet:
'returns a cell 1 to the right of rng, but on Sheet2
Worksheets("Sheet2").Cells(rng.Row, rng.Column + 1)
To use it in your code, I think it's just a case of replacing
If .Value = "" Then .Value = TXT
with
If Worksheets("Sheet2").Range(.Offset(0, 1).AddressLocal).Value = "" Then Worksheets("Sheet2").Range(.Offset(0, 1).AddressLocal).Value = TXT
and replacing
.Value = ""
with
Worksheets("Sheet2").Range(.Offset(0, 1).AddressLocal).Value = ""
(or the same using the Cells construction).
The below will copy the Target.Value into the same cell in Sheet2
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws1 As Worksheet, ws2 As Worksheet
Set ws2 = ThisWorkbook.Worksheets("Sheet2")
If Not Intersect(Target, Me.Range("B5:B50")) Is Nothing And Target.Count = 1 Then
With Target
ws2.Cells(.Row, .Column).Value = .Value
End With
End If
End Sub

Deleting cells and corresponding row if criteria is met

I have a spreadsheet that has columns from A5 to AA5 and has data from A6 to AA10000. In cells A1, a user inputs a value, in cell A2 is a drop box that contains the headers of columns X to AA (A, B, C, D), and in A3 I have a dropdown of logical operators (<,>,<>,=). I'm trying to write a script that goes through columns X to AA and remove the cells that met a criteria that a user sets, e.g. user inputs a value of 300, a header "B" and a logical operator "<" and the macro goes through column Y which has the header "B" and deletes all values that are less than 300, the deletes the row from A to AA.
So far I've attempted this:
Sub removedata()
Dim ws As Worksheet
Dim rng As Range
Dim headerval As Variant
Dim sign As Variant
Dim inputval As Variant
Dim b_header As Range
Dim Cell As Range
Set ws = Worksheets("Sheet1")
Set rng = ws.Range("X5:AA5000")
Set b_header = ws.Range("X5:X5000")
inputval = cells(1, 1).Value
headerval = cells(2, 1).Value
sign = cells(3, 1).Value
For Each Cell In b_header.cells
If (headerval = "B") And (sign = "<") And (inputval < Cell.Value) Then
Cell.Delete
End If
Next Cell
End Sub
I've only attempted it for B column as a test to see whether or not I could get something to happen. When I run this Macro, it just buffers for a second and then nothing else happens.
Any help would be greatly appreciated!
Edit: Actually I realised it deletes the values that are greater than the input (Cell A1), however it only deletes a few of them each time I run it, it also moves the cells below it to its position.
The COUNTIF/COUNTIFS worksheet function accepts and interprets criteria as strings. You can use with Evaluate or directly through an application object.
Option Explicit
Sub delSpecial()
Dim lr As Long, i As Long, c As String, cl As Long
With Worksheets("sheet6")
c = .Cells(3, "A").Value & .Cells(1, "A").Value
cl = Application.Match(.Cells(2, "A").Value, .Rows(5), 0)
lr = Application.Max(.Cells(.Rows.Count, "X").End(xlUp).Row, _
.Cells(.Rows.Count, "Y").End(xlUp).Row, _
.Cells(.Rows.Count, "Z").End(xlUp).Row, _
.Cells(.Rows.Count, "AA").End(xlUp).Row)
For i = lr To 6 Step -1
If CBool(Application.CountIf(.Cells(i, cl), c)) Then
.Cells(i, "A").Resize(1, 27).Interior.Color = vbYellow
'.Rows(i).EntireRow.Delete
End If
Next i
End With
End Sub

Deleting rows based on character length

trying to delete rows with cells with fewer than 2 characters. range("A1") line is highlighted and i have no idea why.
i can run it without the line and for some reason it deletes everything.
any advice greatly appreciated. here's the code:
Option Explicit
Sub way()
Dim cell As Range
Range(“A1").CurrentRegion.activate
For Each cell In Selection
If Len(cell) < 2 Then Selection.EntireRow.Delete
Next cell
End Sub
You can avoid a slow loop by using AutoFilter
This code
Works out the size of the current region from A1
In the next column adds an array formula checking the length of all cells in each row, =MIN(LEN(A1:C1))<2
AutoFilter deletes the True results
code
Sub NoLoops()
Dim rng1 As Range
Dim rng2 As Range
Set rng1 = Range("A1").CurrentRegion
Set rng2 = Range(Cells(1, rng1.Columns.Count + 1), Cells(rng1.Rows.Count, rng1.Columns.Count + 1))
ActiveSheet.AutoFilterMode = False
With rng2
.Formula = "=MIN(LEN(RC[-" & rng1.Columns.Count & "]:RC[-1]))<2"
.FormulaArray = .FormulaR1C1
.Value = .Value
.AutoFilter Field:=1, Criteria1:="TRUE"
.EntireRow.Delete
End With
ActiveSheet.AutoFilterMode = False
End Sub
Give this a try
Sub mysub()
Dim r As Range
Dim i As Double
Dim rcount as Double
Dim mybool As Boolean
Set r = Range("A1").CurrentRegion
i = 1
mybool = False
Do
rcount = r.Rows.count
For j = 1 To r.Columns.count
If Len(Cells(i, j).Value) < 2 Then
Rows(i).Delete
If rcount = 1 then Exit Sub
mybool = True
Exit For
End If
Next j
If mybool = False Then i = i + 1
mybool = False
Loop While i <= rcount
End Sub
Edit: just to elaborate on why I provided a new code alltogether here - the logic behind the original code is actually flawed anyway.
Consider for instance what happens if you range involves the following consecutive rows
A B C D E
1 ee e eee ee eee
2 f fff fff ff ff
Your code will explore each cell row by row top to bottom, from left to right. So in this example:
when reaching B1, it will delete row 1, and row 2 will be moved to row 1
from there, your loop will pick up from cell C1 - not A1. In other words, it will miss out on exploring the value of cell A1 which should qualify the row for deletion
Sub way()
Dim Cell As Range
For Each Cell In Range("A1").CurrentRegion
If Len(Cell) < 2 Then Cell.EntireRow.Delete
Next Cell
End Sub
#IAmDranged is correct in that when you are deleting a row, the next row will move up and become the current row. The Next cell line will then pass over this row and move to the next row without checking to see if any Cells are less than 2 characters in length.
Another method for this would be to leave the Delete method until after the Cells with fewer than 2 characters have been found:
Sub way()
Dim cell As Range
Dim deleteRange As Range 'This will be used to store the Cells found
Range("A1").CurrentRegion.Activate
For Each cell In Selection
If Len(cell) < 2 Then
If deleteRange Is Nothing Then
' If this is the first cell found, then Set deleteRange to this cell
Set deleteRange = cell
Else
' Any cells found after the first, we can use the
' Union method to add it to the deleteRange
Set deleteRange = Application.Union(cell, deleteRange)
End If
End If
Next cell
' Once all cells have been found, then Delete
deleteRange.Delete
End Sub

Resources