I have a problem for which I haven't been able to find a solution.
I have a spreadsheet with 5 -> 10? columns of data. All of them different, but some columns are related to each other (If A3=1, then B3=A and C3=a). Each column contains 3 -> 6 variations of a parameters and I need to create all the possible combinations of them..
Initial data in columns:
Expected result:
Kelvin had almost similar problem previously, but that didn't work for me..
You can do that using SQL with an cross join. Below is a small example I made and tested. You will have to adapt it to your needs. In my example, test1 and test3 are the column names, in the first row of sheet1.
Sub SQLCombineExample()
Dim con
Dim rs
Set con = CreateObject("ADODB.Connection")
con.Open "Driver={Microsoft Excel Driver (*.xls)};" & _
"DriverId=790;" & _
"Dbq=" & ThisWorkbook.FullName & ";" & _
"DefaultDir=" & ThisWorkbook.FullName & ";ReadOnly=False;"
Set rs = CreateObject("ADODB.Recordset")
Set rs = con.Execute("select distinct a.[test1], b.[test3] from [Sheet1$] as a , [Sheet1$] as b ")
Range("f1").CopyFromRecordset rs
Set rs = Nothing
Set con = Nothing
End Sub
From what i see in your pictures, the only item which can change combination is the one in column 4:
(1 ; A ; a ; item4 ; #¤), (2 ; B ; b ; item4 ; &#¤) and (3 ; C ; c ; item4 ; ¤%&)
If this is indeed what your are trying to do the following code should work:
Sub Combination()
Dim i As Integer, j As Integer, k As Integer
For k = 0 To 2 'loop through (1 A a #¤), (2 B b &#¤) and (3 C c ¤%&)
j = 3 'column 4 items
For i = 0 To 6 Step (3) 'loop 3 by 3 (output starts in row 10)
Cells(10 + k + i, 1) = Cells(3 + k, 1)
Cells(10 + k + i, 2) = Cells(3 + k, 2)
Cells(10 + k + i, 3) = Cells(3 + k, 3)
Cells(10 + k + i, 5) = Cells(3 + k, 5)
Cells(10 + k + i, 4) = Cells(j, 4)
j = j + 1
Next i
Next k
End Sub
Sub CopyAllCombinationsToRange()
Dim arSource
Dim arResult
Dim i As Long, j As Long, combinationCount As Long, counter As Long
arSource = Range(Cells(2, 1), Cells(Rows.Count, 5).End(xlUp)).Value
combinationCount = UBound(arSource, 2) * UBound(arSource, 2)
ReDim arResult(4, combinationCount - 1)
For i = 1 To UBound(arSource, 1)
For j = 1 To UBound(arSource, 1)
arResult(0, counter) = arSource(i, 1)
arResult(1, counter) = arSource(i, 2)
arResult(2, counter) = arSource(i, 3)
arResult(3, counter) = arSource(i, 4)
arResult(4, counter) = arSource(j, 5)
counter = counter + 1
Next
Next
Sheet2.Range("A1").Resize(UBound(arResult, 2), 5) = WorksheetFunction.Transpose(arResult)
End Sub
Example
Related
I am trying to perform a sum calculation using a set of rolling values in an array in excel VBA where the "lookback" might differ based on an input. For a simple example, this is what I hope to achieve if the lookback is 3.
[Excel Output Example][1]
This is the code that I am testing out:
Sub testArr(Lookback As Long)
Dim MyArr As Variant, OutputArr As Variant
Dim RowsToExtract As String
Dim i As Long, k As Long, n As Long
Dim SumNum As Double
MyArr = Application.Transpose(Application.Transpose(Range(Range("A2"), Range("A2").End(xlDown))))
n = Application.CountA(MyArr)
ReDim OutputArr(1 To n)
For i = Lookback To n
For k = i - Lookback + 1 To i
RowsToExtract = RowsToExtract & k & ","
Next k
RowsToExtract = Left(RowsToExtract, Len(RowsToExtract) - 1)
OutputArr(i) = Application.Sum(Application.Index(MyArr, Array(RowsToExtract), 0))
Next i
Range(Range("B2"), Range("B2").Offset(n - 1)) = OutputArr
End Sub
However I get the error Run-Time error '13': Type mismatch.
I could do this if I use the range/offset method like:
Sub testRange(Lookback As Long)
Dim InputRg As Range
Dim i As Long, n As Long
Set InputRg = Range("A2")
n = Application.CountA(Range(InputRg, InputRg.End(xlDown)))
For i = Lookback To n - 1
Range("B2").Offset(i - 1) = Application.Sum(Range(InputRg.Offset(i - 1), InputRg.Offset(i - Lookback)))
Next i
End Sub
As the list gets enormously large and several other calculations are needed (not only sum), it takes quite long if I use range/offset method. Hence I am trying to see if using the arrays will speed things up but I am unable to slice(?) a dynamic set of values from the array. Is there a way to go about doing this?
Try this (processes 1 048 576 values in 2,4 sec.):
Option Explicit
Sub RollingSum()
Const Lookback = 7
Dim cnt, cntB, i, ssum
Dim a(), b()
With ActiveSheet
a = Intersect(.Columns(1), .UsedRange)
ReDim b(1 To UBound(a) - Lookback + 1, 1 To 1)
cntB = 1
For i = LBound(a) To UBound(a) - Lookback + 1
cnt = 1
ssum = 0
Do
If cnt > Lookback Then
b(cntB, 1) = ssum
cntB = cntB + 1
Exit Do
End If
ssum = ssum + a(i + cnt - 1, 1)
cnt = cnt + 1
Loop
Next
' output
.Range("B1:B" & UBound(b)) = b
End With
End Sub
Edit2 (universal)
Sub RollingRangeProcessing()
t = Timer
Const Lookback = 7
Dim cnt, cntB, i
Dim a(), b(), c()
With ActiveSheet
a = Intersect(.Columns(1), .UsedRange)
ReDim b(1 To UBound(a) - Lookback + 1, 1 To 1)
cntB = 1
For i = LBound(a) To UBound(a) - Lookback + 1
cnt = 1
ReDim c(1 To Lookback) 'reset array c()
Do
If cnt > Lookback Then '
With WorksheetFunction
'here use the appropriate array processing function
b(cntB, 1) = .Sum(c)
'b(cntB, 1) = .Average(c)
'b(cntB, 1) = .Median(c)
End With
cntB = cntB + 1
Exit Do
End If
c(cnt) = a(i + cnt - 1, 1)
cnt = cnt + 1
Loop
Next
' output
.Range("B1:B" & UBound(b)) = b
End With
Debug.Print "Total time to process " & UBound(a) & _
" values = " & Round(Timer - t, 1) & " sec."
End Sub
Output:
Total time to process 1048576 values = 6,5 sec.
This compares a Customer Name and Part Number on sheet Temp (about 50 rows) to Customer Name and Part Number on sheet Data (about 20,000 rows). If the name and number are found in Data, then the associated information from that same row in Temp is added to Data.
This works great unless a name and number in Temp are not found in Data. When that occurs, a "Subscript out of range" error is generated. To me, it seems like the code is trying to find that value from Temp, and when it cannot find it, it just gives us and throws the error.
Can the code be revised to say, "Hey, if you cannot match a value, it's okay, just skip it and keep going"?
Sub MergeRMAArray()
'##############################################################################
' Creates arrays from "Temp RMA" & "Data" sheets, then compares rows on RMA and when a match occurs,
' pastes values in temp array. After loops, temp array values paste to "Data" sheet.
'##############################################################################
' If when processed there is an error, and the highlighted section states "Subscript out of range", with i+j
' being larger than the rows shown, then one potential error could be that a part on the RMA tab is not
' present in the Data tab, so the macro keeps searching. Will need to try and fix this on the next revision.
'##############################################################################
'##############################################################################
Set Data = Worksheets("Data")
Set Temp = Sheets("Temp RMA")
Data.Activate
Dim arrA, arrB, arrC As Variant
Dim i, j, k, LastRow2 As Long
LastRow = Data.Cells(Cells.Rows.Count, "A").End(xlUp).Row
LastRow2 = Temp.Cells(Cells.Rows.Count, "A").End(xlUp).Row
arrA = Data.Range("A2:B" & LastRow)
arrB = Temp.Range("A2:H" & LastRow2)
ReDim arrC(1 To LastRow - 1, 1 To 4)
For i = LBound(arrB) To UBound(arrB)
j = 0
For k = LBound(arrA) To UBound(arrA)
If (arrB(i, 1) = arrA(i + j, 1) _
And arrB(i, 2) = arrA(i + j, 2) _
And arrC(i + j, 1) = "") Then
arrC(i + j, 1) = arrB(i, 5)
arrC(i + j, 2) = arrB(i, 6)
arrC(i + j, 3) = arrB(i, 7)
arrC(i + j, 4) = arrB(i, 8)
Exit For
End If
j = j + 1
Next k
Next i
Range("W2").Resize(UBound(arrC, 1), UBound(arrC, 2)).Value = arrC
Erase arrA, arrB, arrC
End Sub
You are copying from "Temp" but arrC size is that of "Data". Need to change the Redim Statement. Also, J will reach 20000 each time (Data rows) at that point it will also add i which and then it will go beyond 20000 (Size of arrC) and hence the "Subscript out of Range" error.
Replace redim an loop with following.
m = 0
ReDim arrC(1 To LastRow2 - 1, 1 To 4)
For i = LBound(arrB, 1) To UBound(arrB, 1)
For j = LBound(arrA, 1) To UBound(arrA, 1)
If arrB(i, 1) = arrA(j, 1) _
And arrB(i, 2) = arrA(j, 2) Then
m = m + 1
arrC(m, 1) = arrB(i, 5)
arrC(m, 2) = arrB(i, 6)
arrC(m, 3) = arrB(i, 7)
arrC(m, 4) = arrB(i, 8)
End If
Next j
Next i
Looking at the number of loops in question (50*20,000 = 1000,000) and comparisons in arrays, suggesting following procedure using Range.Find method along with Range.Offset. This will cause much less loops and comparisons compared to those in the question.
Sub MergeRMAArray()
'##############################################################################
' "Temp RMA" & "Data" sheets, compares rows on RMA and when a match occurs,
' pastes values in temp sheet Columns E:H to "Data" sheet.
'##############################################################################
Dim Data As Worksheet: Set Data = Worksheets("Data")
Dim Temp As Worksheet: Set Temp = Sheets("Temp RMA")
Dim i As Long, j As Long, x As Long, y As Long, k As Long
Dim fRG As Range, outPut As Range, rw As Range
Dim dLR As Long: dLR = Data.Cells(Cells.Rows.Count, "A").End(xlUp).Row - 1
Dim tLR As Long: tLR = Temp.Cells(Cells.Rows.Count, "A").End(xlUp).Row - 1
Dim dRG As Range: Set dRG = Data.Range("A2:B" & dLR)
Dim tRG As Range: Set tRG = Temp.Range("A2:H" & tLR)
'Find the values in Temp sheet col A & B in Data sheet Col A & B
'If found union the range from corresponding row in Temp col E to H
For i = 1 To tLR '50 loops
On Error Resume Next
Set fRG = dRG.Columns(1).Find(What:=tRG(i, 1), After:=dRG(dRG.Rows.Count, 1), _
LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
If Not fRG Is Nothing Then
If tRG(i, 2) = fRG.Offset(0, 1) Then
If outPut Is Nothing Then
Set outPut = tRG(i, 1).Offset(0, 4).Resize(1, 4)
Else
Set outPut = Union(outPut, tRG(i, 1).Offset(0, 4).Resize(1, 4))
End If
End If
End If
Next
'Put all the outPut range values in arrC
Dim arrC
For Each Area In outPut.Areas 'max 50 loops
x = x + Area.Rows.Count
Next
y = outPut.Columns.Count
ReDim arrC(1 To x, 1 To y)
i = 0
For k = 1 To outPut.Areas.Count 'max 50*50 = 2500 loops
For Each rw In outPut.Areas(k).Rows
i = i + 1
arr = rw.Value
For j = 1 To y
arrC(i, j) = Split(Join(Application.Index(arr, 1, 0), "|"), "|")(j - 1)
Next
Next
Next
'Copy outPut values (stored in arrC) to Range("W2") in Data sheet
Data.Range("W2").Resize(x, y).Value = arrC
End Sub
this is part of my code that i am working with and I have one problem. I have array with values (masyvas) and i started new loop to find other values by using masyvas(i,1) values and after that i need that new values would be printed in masyvas(i,2) and i need to group them. It need to look like this:
991988 Gaz.duon.sk"Giros"gaiv.g.1,5L 5_PETØFLAT1,5
PALINK
117388 Silp.gaz.nat.min.v"Tiche'1,5L 5_PETØFLAT1,5
PALINK
RIMI LIETUVA
ŠIAULIŲ TARA
111388 Gaz.nat.min.v"Tiche" 1,5L pet 5_PETØFLAT1,5
PALINK
AIBĖS LOGISTIKA
AIBĖS LOGISTIKA
RIMI LIETUVA
ŠIAULIŲ TARA
How it looks now from marked 1 it goes wrong
Data sheet where i get array values
Here is part of my code where i have this problem now it prints new values next to masyvas(i,2) but not below as I need.
lastrow2 = Sheets("lapas").Cells(Rows.Count, 1).End(xlUp).Row
rub = lastrow2
cub = 3
ReDim masyvas(1 To rub, 1 To cub)
For i = 1 To rub
For j = 1 To cub
masyvas(i, j) = Sheets("lapas").Cells(i, j).Value 'array gets values from filtered data in AKCIJOS sheet
Next
Next
Sheets("lapas").Range("A1:C100").Clear
For i = 1 To rub Step 1
Set rng2 = grafikas.Cells(6 + h, 2)
prekeskodas = masyvas(i, 1)
For m = 2 To lastrow
If akcijos.Cells(m, 8) >= laikas And akcijos.Cells(m, 8) <= laikas2 Then
If prekeskodas = akcijos.Cells(m, 4) Then
grafikas.Cells(7 + r, 2).EntireRow.Select
Selection.Insert Shift:=xlDown
grafikas.Cells(7 + r, 3) = akcijos.Cells(m, 3)
r = r + 1
h = r
End If
End If
Next m
For j = 1 To cub Step 1
rng2.Offset(i - 1, j - 1).Value = masyvas(i, j)
Next
Next
You didn't provide any screenshot of your data, so it's hard to say what exactly is your problem and desired output, but try the code below. I marked changed lines.
For i = 1 To rub
prekeskodas = masyvas(i, 1)
For m = 2 To lastrow
If akcijos.Cells(m, 8) >= laikas And akcijos.Cells(m, 8) <= laikas2 Then
If prekeskodas = akcijos.Cells(m, 4) Then
'masyvas(i, 2) = masyvas(i, 2) & akcijos.Cells(m, 3)
masyvas(i, m) = masyvas(i, m) & akcijos.Cells(m, 3) '<------
End If
End If
Next
For j = 1 To cub
rng2.Offset(j - 1, i - 1).Value = masyvas(i, j) '<-----
Next
Next
Problem
How can you horizontally align values in separate columns, and apply a dynamic formula? Preemptive thank you for any help or clues! The code pasted below works, in so far as it reaches halfway to the end destination. But how to accomplish the last two objectives?
1) Sum each range
2) Align the ranges horizontally
A sample sheet containing customer id, item and prices. Sales from Monday on the left, Tuesday on the right.
Current results
Desired results
Align cust id on rows A and E, with an associated sum. Notice how each yellow line contains cust id for identification, as well as associated Sum total.
Existing VBA Code
Sub AlignAndMatch()
'backup sheet
ActiveSheet.Copy after:=Sheets(Sheets.Count)
'Insert rows where current cell <> cell above
Dim i, totalrows As Integer
Dim strRange As String
Dim strRange2 As String
'----------------------------------------
'Monday sort table
Range("A2:C65536").Select
Selection.Sort Key1:=Range("A2:C65536"), Order1:=xlAscending, Header:=xlGuess, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
'Monday insert loop
totalrows = ActiveSheet.Range("A65536").End(xlUp).Offset(1, 0).Row
i = 0
Do While i <= totalrows
i = i + 1
strRange = "A" & i
strRange2 = "A" & i + 1
If Range(strRange).Text <> Range(strRange2).Text Then
Range(Cells(i + 1, 1), Cells(i + 2, 3)).Insert xlDown 'think cells ~A1:C2 insert
totalrows = ActiveSheet.Range("A65536").End(xlUp).Offset(1, 0).Row
i = i + 2 'for insert 2 rows
End If
Loop
'Monday footer row loop
totalrows = ActiveSheet.Range("A65536").End(xlUp).Offset(0, 0).Row
i = 0
Do While i <= totalrows
i = i + 1
If IsEmpty(Range("A" & i).Value) And Not IsEmpty(Range("A" & i + 1).Value) Then
Range("A" & i).Value = Range("A" & i + 1).Value
Range("B" & i).Value = "Sum"
End If
Loop
'----------------------------------------
'Tuesday sort table
Range("E2:G65536").Select
Selection.Sort Key1:=Range("E2:G65536"), Order1:=xlAscending, Header:=xlGuess, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
'Tuesday insert loop
totalrows = ActiveSheet.Range("E65536").End(xlUp).Offset(0, 0).Row
i = 0
Do While i <= totalrows
i = i + 1
strRange = "E" & i
strRange2 = "E" & i + 1
If Range(strRange).Text <> Range(strRange2).Text Then
Range(Cells(i + 1, 5), Cells(i + 2, 7)).Insert xlDown 'think cells ~A1:C2 insert
totalrows = ActiveSheet.Range("A65536").End(xlUp).Offset(1, 0).Row
i = i + 2 'for insert 2 rows
End If
Loop
'Tuesday footer row loop
totalrows = ActiveSheet.Range("E65536").End(xlUp).Offset(0, 0).Row
i = 0
Do While i <= totalrows
i = i + 1
If IsEmpty(Range("E" & i).Value) And Not IsEmpty(Range("E" & i + 1).Value) Then
Range("E" & i).Value = Range("E" & i + 1).Value
Range("F" & i).Value = "Sum"
End If
Loop
End Sub
If I needed something like that I might think twice what I want and why: if the original day lists don't come from somehwere, you could put everything into one list and make some pivots...
But. Here's some idea, playing with the arrays again and there's probably work to do, but does this help:
Option Base 1
Sub ReLists()
Dim ListSheet As Worksheet
Dim DayCorners() As Range
Dim Day()
Dim Days As Integer
Dim CustIDs()
Dim CustomerRow() 'for placement in the final list
Dim DayList()
Dim MaxCustIDs As Integer
Dim NewCustID As Boolean
Days = 2
MaxCustIDs = 5
ReDim DayCorners(Days)
ReDim Day(Days)
ReDim CustomerRow(MaxCustIDs + 2)
CustomerRow(1) = 0
ReDim CustIDs(MaxCustIDs)
ReDim DayItems(1, 1)
Set ListSheet = Worksheets("Sheet1")
Set DayCorners(1) = ListSheet.Range("A2")
Set DayCorners(2) = ListSheet.Range("E2")
For d = 1 To Days
With ListSheet.Sort
.SortFields.Clear
.SortFields.Add Key:=DayCorners(d)
.SetRange Range(DayCorners(d), DayCorners(d).End(xlDown).Offset(0, 2))
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.Apply
End With
Day(d) = Range(DayCorners(d), DayCorners(d).End(xlDown).Offset(0, 2))
If UBound(Day(d), 1) > UBound(DayItems, 2) Then
ReDim DayItems(Days, UBound(Day(d)))
End If
Next d
CustIDCount = 0
For d = 1 To Days
For r = 1 To UBound(Day(d), 1)
NewCustID = True
For u = 1 To UBound(CustIDs)
If CustIDs(u) = Day(d)(r, 1) Then NewCustID = False
Next u
If NewCustID Then
CustIDCount = CustIDCount + 1
CustIDs(CustIDCount) = Day(d)(r, 1)
End If
Next r
Next d
With Worksheets.Add(After:=Worksheets(ListSheet.Index))
Set DayCorners(1) = .Range("A2")
Set DayCorners(2) = .Range("E2")
End With
ReDim DayList(Days, CustIDCount, 100, 3)
For d = 1 To Days
For c = 1 To CustIDCount
rc = 1
For r = 1 To UBound(Day(d), 1)
If Day(d)(r, 1) = CustIDs(c) Then
DayList(d, c, rc, 1) = Day(d)(r, 1)
DayList(d, c, rc, 2) = Day(d)(r, 2)
DayList(d, c, rc, 3) = Day(d)(r, 3)
rc = rc + 1
End If
Next r
If CustomerRow(c) + rc + 2 > CustomerRow(c + 1) Then
CustomerRow(c + 1) = CustomerRow(c) + rc + 1
End If
Next c
If CustomerRow(c - 1) + rc + 2 > CustomerRow(c) Then
CustomerRow(c) = CustomerRow(c) + rc
End If
Next d
For d = 1 To Days
With DayCorners(d).Offset(-1, 0).Range("A1:C1")
.Value = Array("cust id", "item", "Price")
'formatting
End With
For c = 1 To CustIDCount
SumFormula = "=SUM(R[1]C:R[" & (CustomerRow(c + 1) - CustomerRow(c) - 1) & "]C)"
With DayCorners(d).Offset(CustomerRow(c), 0).Range("A1:D1")
If Not IsEmpty(DayList(d, c, 1, 1)) Then
.Value = Array(CustIDs(c), "Sum", SumFormula, "")
End If
.Interior.Color = 65535
End With
For rc = 1 To UBound(Day(d), 1)
If IsEmpty(DayList(d, c, rc, 1)) Then Exit For
DayCorners(d).Offset(CustomerRow(c) + rc, 0) = DayList(d, c, rc, 1)
DayCorners(d).Offset(CustomerRow(c) + rc, 1) = DayList(d, c, rc, 2)
DayCorners(d).Offset(CustomerRow(c) + rc, 2) = DayList(d, c, rc, 3)
Next rc
Next c
Next d
End Sub
I believe the solution is to simulate an SQL full outer join, via VBA. I'll start hacking away at it. Should be a fun personal challenge. I'll try to update this answer once I find the final solution.
The direction I'm following is here.
I've a worksheet named "GetData". In this worksheet are more columns.
A(Names)|B... |C(Center)
++++++++|+++++++|+++++++++
Alpha | |100-Base
Beta | |110-2nd
Charly | |100-Base
Now I want sort them into another worksheet named "Overview" like this:
A(Grouped)
++++++++++
100-Base
Alpha
Charly
110-2nd
Beta
I think I need an array, but I don't know how to build this. I tried this for beginning:
Sub unique4()
Dim arr As New Collection, a
Dim aFirstArray() As Variant
Dim i As Long
Dim LastRow As Long
LastRow = Worksheets("GetData").Cells(Worksheets("GetData").Rows.Count, "C").End(xlUp).Row
aFirstArray() = Worksheets("GetData").Range("C2:C" & LastRow).Value
On Error Resume Next
For Each a In aFirstArray
arr.Add a, a
Next
For i = 1 To arr.Count
Cells(i, 1) = arr(i)
Next
End Sub
Here is one without arrays,
Sub unique4()
Dim i As Long
Dim lastrow As Long
Dim j As Long
Dim tws As Worksheet
Set tws = Sheets("Sheet2")'Change to desired sheet output name.
j = 1
With Sheets("GetData")
lastrow = .Cells(.Rows.Count, "C").End(xlUp).Row
For i = 1 To lastrow
If i <> 1 Then
If .Cells(i, 3) <> .Cells(i - 1, 3) Then
tws.Cells(j, 1) = .Cells(i, 3)
j = j + 1
tws.Cells(j, 1) = .Cells(i, 1)
j = j + 1
Else
tws.Cells(j, 1) = .Cells(i, 1)
j = j + 1
End If
Else
tws.Cells(j, 1) = .Cells(i, 3)
j = j + 1
tws.Cells(j, 1) = .Cells(i, 1)
j = j + 1
End If
Next
End With
End Sub
One Caveat, you will need to sort the data on column C to make work.
this should solve your issue with just an Excel formula. If you want a vba solution, that is of course possible too.
In your new sheet, in column A Setup it like:
Column A
1
1
2
2
3
3
You can get this format by entering the following in Cell A3 and pull it down as far as you need (set Cell A1, A2 with a value of 1).
=A1+1
In Column B you are going to enter an Indirect Formula. In cell B1, put this:
=INDIRECT("Sheet1!A"&A1)
In Cell B2, put this:
=INDIRECT("Sheet1!C"&A2)
Column A will keep track of which row to pull from, then the indirect formula will dynamically build the formula to get the value. Hope it helps!
Without sorting any data or something like that:
Sub test()
Dim LastRow As Long, i As Long, j As Long, k As Long, chkB As Boolean
Dim wsGet As Worksheet, wsPut As Worksheet
Set wsGet = ThisWorkbook.Worksheets(1)
Set wsPut = ThisWorkbook.Worksheets(2)
Const FirstRow As Long = 3
LastRow = wsGet.Range("C" & wsGet.Rows.Count).End(xlUp).Row
For i = FirstRow To LastRow
chkB = True
For j = FirstRow To i - 1
If wsGet.Cells(i, 3) = wsGet.Cells(j, 3) Then chkB = False: Exit For
Next
If chkB Then
k = k + 1
wsPut.Cells(k, 1) = wsGet.Cells(i, 3)
For j = i To LastRow
If wsGet.Cells(j, 3) = wsGet.Cells(i, 3) Then
k = k + 1
wsPut.Cells(k, 1) = wsGet.Cells(j, 1)
End If
Next
End If
Next
End Sub