- 최초 작성일: 2000-07-19
- 최종 수정일: 2026-09-30
- 조회수: 11 회
- 작성자: 권현욱 (엑셀러)
- 강의 제목: 중복아이템 추려내기 종합정리
들어가기 전에
오래 전 엑셀 파일로 진행하던 강의를 웹 글로 복원한 것이라, "버튼을 눌러보세요"처럼 엑셀에서 직접 조작하라는 표현이 남아 있을 수 있습니다. 결과는 본문의 그림이나 표로 확인하실 수 있으며, 현재와 맞지 않는 내용은 보완하되 전반적인 스타일은 당시의 갬성(?)을 유지했습니다.
지난 강좌(VB0029)에서 컬렉션 오브젝트를 사용하여 "UserForm 상에서의 중복 아이템 제거" 방법에 대해 설명을 드렸더니 이런 질문을 하시는 분이 있더군요.
"거참 신기하기는 한데… 유저폼에서 중복 아이템 제거하는 것 말고, 일반 워크시트 상에서
아이템들을 겹치지 않게 출력하는 방법은 없습니까?"
이래서 응용이 중요한 것이지요!! 프로그래밍에 익숙해지는 가장 빠른 방법은 아마도 남이 짜놓은 코드를 잘 정리한 후 각 프로퍼터, 메서드 등을 이렇게도 수정해 보고, 저렇게도 바꾸어 보고… 하면서 어떻게 변하는지를 살펴보는 것이 아닌가 생각합니다.
컬렉션 오브젝트의 사용법을 알려드렸으니 그걸 유저폼 상의 리스트 박스에 표시하는 대신 셀에다가 출력하는 것은 오브젝트만 바꾸면 되는 것이지요. 아래의 버튼을 누르세요.
중복아이템 추려내기 종합정리
핵심 요약: 중복 제거 종합정리
데이터가 많아질수록 컬렉션 방식이 반복문 방식보다 훨씬 빠릅니다. 출력 대상만 바꾸면 셀에도 출력할 수 있습니다.
- 1단계: Timer로 시작 시각을 저장하고 종료 시각과의 차이로 실행 시간을 구합니다.
- 2단계: 컬렉션에 Key와 함께 추가해 중복을 제거합니다.
- 3단계: For Each로 컬렉션을 순회하며 셀에 출력합니다.
Step 1: 실행 속도 비교하기
어떻습니까? 비교적 적은 양(그래도 천 레코드는 넘지요)의 데이터 이지만 어떤 로직을 사용하느냐에 따라 엄청난 실행 속도의 차이를 보이지요? 여러분이라면 어떤 것을 사용하시겠습니까? 이제 당연히 두번째의 것을 택하시겠지요? 만약 ExtractOneItem1()과 같은 무대뽀 로직(?)으로 프로그램을 만들어서 팔아먹었다간 바로 재떨이가 날아올 것입니다. ^___^ 무대뽀 로직을 너무 안좋게만 얘기를 했는데 이 무대뽀 로직도 자료의 양이 적을 경우에는 아주 훌륭하게 작동을 합니다. 천덕꾸러기는 아니지요!
Step 2: ExtractOneItem1 (반복문 방식)
ExtractOneItem1()에 대해서는 지난 강좌에서 설명을 드렸고... ExtractOneItem3()에 대해 설명을
Sub ExtractOneItem1()
'//Extract one item from duplicated items.
'//No need to Sort in advance! //By Exceller
Dim intNum As Integer
Dim intNum1 As Integer
Dim intNum2 As Integer
Dim rngSource As Range
Dim rngTarget As Range
Dim Msg As String
Dim lngLast As Long
Dim sngStart As Single
Const str1 = "대상 영역의 첫 셀을 선택하세요." & vbCr & _
"A1 셀을 선택하시는 것이 좋겠군요"
Const str2 = "어느 셀에서부터 출력할까요?" & vbCr & _
"B1 셀을 선택하세요!"
On Error Resume Next
Set rngSource = Application.InputBox(str1, "대상셀 선택//Exceller", "A1", Type:=8)
Set rngTarget = Application.InputBox(str2, "출력기준셀 선택//Exceller", ActiveCell.Address, Type:=8)
If rngSource Is Nothing Or rngTarget Is Nothing Then
MsgBox """취소""를 선택하셨으므로 종료합니다", , "범위 지정 오류//Exceller"
Exit Sub
End If
lngLast = rngSource.Parent.Cells(rngSource.Parent.Rows.Count, rngSource.Column).End(xlUp).Row - rngSource.Row
sngStart = Timer
For intNum = 0 To lngLast
For intNum1 = 0 To intNum - 1
If rngSource.Offset(intNum, 0) = rngSource.Offset(intNum1, 0) Then GoTo et
Next intNum1
rngTarget.Offset(intNum2, 0) = rngSource.Offset(intNum, 0)
intNum2 = intNum2 + 1
Application.DisplayStatusBar = True
Application.StatusBar = intNum2 & "개의 Item 추출..."
If intNum2 > 15 Then ActiveWindow.SmallScroll down:=1
et:
Next intNum
Range("A1").Select
Application.StatusBar = ""
Msg = "총 " & intNum2 & "개의 Item을 추출하였으며," & vbCr
Msg = Msg & "경과된 시간은 " & Format(Timer - sngStart, "0.00") & "초 입니다" & vbCr & vbCr
Msg = Msg & "이제 [ExtractOneItemⅢ] 버튼을 눌러 보세요..."
MsgBox Msg, , "//Exceller"
End Sub
Step 3: ExtractOneItem3 (컬렉션 방식)
드리겠습니다. 코드를 보세요.
Sub ExtractOneItem3()
'//This is another sample of Extracting Unique Items from duplicated items.(//Exceller)
Dim rngAll As Range
Dim rngCell As Range
Dim rngOut As Range
Dim X As New Collection
Dim varItem
Dim sht As Worksheet
Dim intNum As Integer
Dim sngStart As Single
Dim Msg As String
'//프로그램에서 사용될 변수를 정의해 주었습니다. 다른 변수들은 VB0029 강좌와 같은데
'//sngStart라는 변수가 하나 다르지요? 이것은 프로그램의 실행속도를 측정하기
'//위한 변수입니다.
Set sht = Sheets("Sheet2")
Set rngAll = sht.Columns(1).SpecialCells(xlTextValues)
On Error Resume Next
Set rngOut = Application.InputBox("출력 셀을 선택하세요:", "//Exceller", ActiveCell.Address, Type:=8)
If rngOut Is Nothing Then
MsgBox """취소""를 선택하셨으므로 종료합니다", , "범위 지정 오류//Exceller"
Exit Sub
End If
sngStart = Timer
'//Timer라는 것은 '자정 이후 경과한 시간(정확하게는 초)을 나타냅니다.
'//즉, 현재 시간을 sngStart 변수에 저장을 해 두고…
For Each rngCell In rngAll
X.Add rngCell.Value, CStr(rngCell.Value)
'//컬렉션 오브젝트에 대한 설명은 지난 강좌의 그것을 그대로 사용했으니 자세한 내용은
'//VB0029 강좌편을 참고하세요.
Next rngCell
For Each varItem In X
rngOut.Offset(intNum, 0) = varItem
intNum = intNum + 1
'//나머지 부분은 VB0029와 똑같은데 셀에다가 출력을 해야 하므로 접근하는 오브젝트가
'//조금 다르지요? 즉, VB0029에서는 UserForm 상의 ListBox 오브젝트에 출력을 했었으나
'//이번에는 셀에 출력을 하므로 이 부분만 위와 같이 수정을 해 주면 되겠군요.
Next varItem
Msg = "이번에는 " & Format(Timer - sngStart, "0.00") & "초가 걸렸습니다" & vbCr & vbCr
Msg = Msg & "굉장하지요? 그냥 무대뽀(?)로 할 때와 비교해서" & vbCr
Msg = Msg & "엄청나게 시간이 단축되었지요?"
MsgBox Msg, , "//Exceller"
End Sub
마무리
알고보면 아주 쉬울 것입니다. 알고보면 쉬운데 알기까지의 과정이 쉽지가 않은 것이지요. (쓰다보니 말장난 같은데 사실이 그런 것을 어쩌겠습니까?(^^;)) 처음 볼 때 이해가 안되면 뒀다가 시간이 좀 지난 다음에 두 번, 세 번 계속 들여다 보세요.
다음 시간에 또…
정리 — 중복 제거 종합정리 핵심
| 구분 | 사용한 코드 | 역할 |
|---|---|---|
| 시간 측정 | sngStart = Timer | 시작 시각 저장 |
| 경과 시간 | Format(Timer - sngStart, "0.00") | 걸린 초 |
| 중복 제거 | X.Add rngCell.Value, CStr(rngCell.Value) | Key로 중복 방지 |
| 셀 출력 | rngOut.Offset(intNum, 0) = varItem | 고유 항목 출력 |
| 범위 입력 | Application.InputBox(..., Type:=8) | 출력 셀 선택 |
자주 묻는 질문 (FAQ)
Q1. 반복문 방식은 언제 적합한가요?
자료의 양이 적을 때에는 아주 훌륭하게 작동하므로 천덕꾸러기는 아닙니다.
Q2. Timer는 무엇을 돌려주나요?
자정 이후 경과한 시간(초)을 돌려줍니다.
Q3. 컬렉션 결과를 유저폼 대신 셀에 출력하려면 어떻게 하나요?
출력하는 오브젝트만 ListBox에서 셀로 바꾸면 됩니다.
마치며
알고 보면 쉬운데 알기까지의 과정이 쉽지 않습니다. 이해가 안 되면 시간이 지난 뒤 다시 들여다 보시기 바랍니다.