• Hi All

    Please note that at the Chandoo.org Forums there is Zero Tolerance to Spam

    Post Spam and you Will Be Deleted as a User

    Hui...

  • When starting a new post, to receive a quicker and more targeted answer, Please include a sample file in the initial post.

help with matching values in two columns

Hi ,

I am as lost about John's workbook as you are ; hopefully he will explain everything. In the meantime , I am going ahead with the code , and I'll upload the revised code as soon as it works correctly.

Narayan
 
Hi John ,

I would only like to comment that a formula such as :

=MATCH(B2:B37,$B$2:$B$37,0)

is the same as :

=MATCH(B2,$B$2:$B$37,0)

as far as your usage is concerned.


Second , when you upload a file , please remove any matter which is not relevant ; I find that in Sheet1 , columns D through H have formulae in them ; when I do a Trace Dependents on any of the cells in these 5 columns , Excel says there are no cells which refer to them. Probably you can confirm or clarify this.

Narayan

Hi Narayan first on your explanation of the formula usage above ... i get a little mixed up at times when im working out formulas especially when i also trying to visualize the overall problem lol i had ment to use the match(B2,$B$2:$B$37 which i used on sheet 2

Sorry about uploading the file with non relevant columns ... again i used these to help me visualize aspects of what i was doing ... and in future i will delete as i can imagine it makes things confusing.

The file i uploaded was initally not intended to be posted as i was just doing a rough job of looking at the problem .. it was like a rough sketch to visualize how things worked and looking for a solution.
The reason i posted was i saw a large difference in how many got the holiday the required based on the supplied info when holidays are allocated and from emailing each other looking for matches .. which seems very few

Compared to how many could have the choice they wanted if all employees could first fill in a sheet and be matched up to the holiday slots ....
so when i saw the large difference i posted in case it was relevant ....

Now looking at the new post below and the new information supplied my initial assement might not be relevant ... but i will look at it and if i repost i will aim to have only relevant columns with connected data in them.
 
I am lost again with who swaps with who, can you expain sheet2's output the speciifcally the 25 matches, also leave letters are assigned when they are hired

sorry for the confusion ... i will explain my example (but it is seperate from what Narayank991 is doing for you so no need to get my suggestion confused with the macro he is doing for you)

Basically sheet 1 is how i vision your present situation eg column A (sheet 1)are 36 employees and there id are 1 to 36
colum B is the Letter code for the holiday period they are assigned eg employee 3 is assigned A.
column C is there preferred choice for that year .. eg employee 3 would like to be C
so Employee 3 is assigned A but would like C

ok now we jump to column I ... this show all the employees who are assigned to A and what there choice would be .... they are 5 here who got assigned A but 2 would like C one would like D and 2 would like E
Column J then is the employees who are assigned B and there choices
column K employees who are assigned C and there choices ...
finishing at N for employees who are assigned F and there choices.

Column G shows how many got assigned a letter that actually matched their choise eg Employee 1 is assigned A and his choice was A .. This column shows that overall 8 out of 36 are happy .... which is very low
i then manually went through columns I to N to find matches for the other employees and found 8 matches (the highlighted cells)
this gives only a total then of 16 out of 36 employees sorted again a very low total

Ok on to sheet 2 ...
Here we have same 36 employees with same id in Column D .... then in column E we have there choice of holidays (these are the same choices as used in colum C sheet1)
Then in Column G i pulled out all those who wanted a A as there first choice
in Column H i pulled out all those looking for C as their choice ...Column I all those who wanted a D as their choice ... Column j all those who wanted a E as their Choise ... Column K all those who wanted a B as their Choise ... Column L all those who wanted a F as their choise

i then filled these into a box representing the 6 choices A to F and with 36 employees gave 6 slots per letter ... this then showed doing it this way 25 employees get to have the holiday of their choice ...
Which is far more getting what they want then the way its being done

i then added a second choice in column C .... and pulled out a further 5 matches and highlighted these under the box for first choices ...
giving a total of 30 out of 36 happy with their holiday choice for that year

and thought this might be worth looking at seeing as your present systm gives 16 of 36 which is less than half ... and this way could possibly offer nearly full employee getting the holiday they would like .

This was draw from the initial information you posted and i hope its easier to understand now.

Based on the new information you posted which is slightly different i havent had a chance to really look to see if this would work the same but did notice if you were a employee assigned to C you would get a holiday Next year (between october and End of November) but would get no holidays the following year ??
so this makes the situation different.

Anyway if you want more suggestion based on this im happy to offer some but im sure Narayank991 will be able to sort this for you ... i dont wish to confuse the situation for you more than i have allready done
 
Hi ,

Sorry , but I realized that the macro is not doing what you want done ; give me some more time to revise the code.

Narayan
Hi Narayan,

Just wondering if you have had any time to revise your the code as yet.

I really think the best way to resolve is to firstly/remove all the direct 2X way swaps.


1. Staff 1 has “A” wants “B”

2. Staff 5 has “B” Wants “A” then remove all 2 way matches


Then Search for all combinations of matches in the following way (not that I know how to do this in VBA): take this scenario


A-D

B-A

C-F

E-G

C-H

F-A

D-B

G-C


Presently, It would be difficult to know “who can swap with who” unless you run through each value line by line and see check for this which is very complicated.

But, I think there is a much easier way to find out really quickly.

Which is to use code to search for matches such as:

With Range("C2:C" & lRow)

.Formula = Replace("=IFERROR(""A""&AGGREGATE(15,6,ROW($B$2:$B$@)/($A$2:$A$@=B2),COUNTIF(B$2:B2,B2)) & ""-A""&ROW(),"""")", "@", lRow)

.Value = .Value

End With


Then from the results above do a search for matching values that are in each column that are on the same row, meaning Column A = (A,B,D) & on the same rows we have (D,B.A) in column B and if both columns are true, then we know we can safely swap knowing that each person is happy.

Attached is an example what I am describing! Please let me know what you think of this










Many thanks
 

Attachments

  • chandoo same row match.xlsx
    12.6 KB · Views: 1
Hi ,

Sorry but I have not looked at your problem thereafter. Can you wait till tomorrow ? I'll post the revised code , either implementing what you have just now described , or anything else that comes to mind.

Narayan
 
Hi ,

Can you see the attached file ?

I have tried it with both the sets of data , the one in the file , as well as the one in your first upload. See if you can cross-check it with other data.

Narayan
 

Attachments

  • RealisticData.xlsm
    38.7 KB · Views: 7
Excellent and Many Thanks for your code, I did test the code with some data,
But some how it seemed to miss the following 4 X way swap.
E G
G F
F G
G E

Has Wants
B A
F G
F C
E2 C2
A1 G1
C2 B1
G E
A2 F1
E1 B1
E2 B1
G2 C2
C2 B1
G1 E2
B1 F1
F2 B1
F1 C2
B1 F1
C2 D1
G F
A2 F1
E1 B1
F1 C2
G2 D2
G2 B1
C2 G2
B2 F1
E G
A1 C2
A2 B1
G E
E1 F1
E2 B1
A C
F2 A2
G2 E2
E1 D1
G1 E2
E2 C1
A1 D1
A2 C2
B C
A1 E2
E2 C1
F A
E2 C2
F2 G1
 
Hi ,

I am sorry , but I should have explained ; I have created a named range called HasWants , which has the following formula in its RefersTo box :

=Sheet2!$B$2:$C$47

When ever you are going to run the macro on a new data set , ensure that this definition is updated to suit the extent of the new data set.

See if the attached file covers everything in your posted data set.

Narayan
 

Attachments

  • RealisticData.xlsm
    39.9 KB · Views: 4
sorry, thanks for the info...silly me, as I was still trying to figure out why when I exported/imported your code it wouldn't work on other workbooks. Looks all good, I will give it a try more extensivley on other test data when I get back from work ..Many thanks again for all you hard work
 
Last edited:
sorry about the delay in getting back to you, I have now tested the code with sample data (with whole numbers only) and unfortunately it doesn't catch all the matches and only seems to sort some but not all of the data. please See attached sample. I think your VBA should firstly sort AlphaNumerically, then search and process the straight 2Xway swaps. Then continue with the rest of the searches 3X way, 4Xway, Etc.
 

Attachments

  • RealisticData1.xlsm
    39.5 KB · Views: 2
Hi ,

No problem ; it is late tonight , and I'll post the revised code tomorrow.

Narayan

Could you please step through the Attached Sub before adjusting your code named "recordit", as I think this method in VBA is the way forward. Wish I was good enough to do it in loops like you programmers can do. :(
 

Attachments

  • SubrecordIt.zip
    82.8 KB · Views: 3
Hi ,

I have an idea that the optimal solution may not be possible , unless we use recursion , something which I am not good at.

If you are willing to accept a sub-optimal solution , then one practical method is to run the macro once , extract the solution to a separate sheet , and then run the macro a second time on the remaining data ; this may or may not give further matches. Repeating this two or three times will give nearly 90 % of all possible results. Is this possible ?

I have revised the code but it still seems to be missing some solutions. If you want try it out and see.

I'll continue looking for whether there is a way to get an optimal solution in some other fashion.

Narayan
 

Attachments

  • RealisticData1.xlsm
    42.1 KB · Views: 5
Hi, firstly thanks, I tried running as you suggested the code a second time around, but nothing happens. I guess it's because if it didn't find the first time around then it won't find any more swaps, No matter how many times I try, yet there was clearly one other available a 2X way or 3Xway depending on which way it searched :...wonder why it missed these especially the straight swap AH<>HA...whats your thought on that..:)

HAS <>WANTS

A <> F
F <> H
H <> A

or

A <> H
H <> A
 
Hi ,

Running the macro a second time will give results if you sort the data before running the macro so that the arrangement of the data changes ; if the data remains in its original order , then running it a second time will not produce any new results.

If I could tell you why the code misses out on these obvious results , I would have corrected it !

Narayan
 
Hi ,

I will try to explain why the code misses what is obvious to us ; the way the code works is like this :

1. Start with the first row , and work downwards till the last row of data is completed.

Suppose we take the first two items , which are A and B.

What we now do is look at all of the items starting with the first row ; where the row has already been considered we skip that row ; thus for the first two items , we will skip row 1.

We come to row 2 and look at the items F and G ; since our starting items are A and B , we need to look at items where the Has data is B. Since we have F and G in this case , we can ust skip this item and go to the next item. In this manner , we will pass over all of the items till we come to the row which has B and A ; since the Has data matches with our Wants , and since the Wants matches with our Has , this particular search has ended here itself.

We now remove both these rows from our list ; thus rows 1 and 16 are removed.

We now move to row 2 , which has items F and G ; following the earlier described method , we will look for that row where the Has is G ; row 15 matches with items G and H ; we now extend our swap chain and now need to look for a row where the Has is H. Since we might have skipped any such entry in the earlier rows , we start our search for this from the first row , skipping those rows which have already been used to build up our swap chain , which in this case means rows 2 and 15. Thus starting from row 1 and proceeding downwards , we see that row 5 matches with items H and F. Since H matches what we want , and coincidentally the F matches our original Has , our search now ends here.

Now we remove the rows 2 , 5 and 15 from the list.

In this manner we work our way through the entire list.

When we finish covering all of the data , we are left with quite a few entries. Why ?

Let us consider the items in row 14 which are F and H ; when we start looking for rows with Has equal to H , we come across row 11 which has H and D. Resetting our Wants to D , we look for a Has equal to D ; there is no such row !

This is where recursion comes in ; when we don't have a match after looking through all of the data , what we should be doing is backing up ust one step , so that instead of taking the match for F and H as H and D , we should now look for the next row whose Has equals H ; this would be another H and D in row 23 ; this would also lead us nowhere , and we would again back up.

Next we would come across H and E in row 26 ; this would also end in a blind alley , which would mean that we again back up. Next would come H and B in row 40 , again leading us nowhere.

Backing up , we would come to H and A in row 41 ; now we would look for a Has equal to A ; this would bring us to A and C in row 27 and A and G in row 28 , both of which would lead us up a blind alley.

Backing up each time , we would now come to A and F in row 30 , which would finally complete our swap chain !

All of this would be possible only if we could back up one step each time we come to a blind end ; unfortunately , this can be done only if we use recursion , which I am not good at ; probably if I spend enough time on it , I might be able to come up with code to do this , but I ust don't have that time.

At present the way the code works is that if it comes to a blind end , it ust gives up on that pair and goes on to the next row in the list ; thus , when the items F and H don't find an immediate match , they are skipped , and the code takes up the next row in the list. This results in a pair which could have been matched remaining without a match.

If by coincidence , the pair H and A and the pair A and F had occurred before the other pairs in the list , the code would have found them correctly !

Narayan
 
Hi ,

See this file.

I have been able to reproduce the recursive behaviour using an additional column in the worksheet , and the code works the way I want it to. No obvious matches are being missed.

Unfortunately , I doubt that the result is optimal !

I think it all has to do with the way the data is ordered , but again , ordering the data in a particular way is unlikely to improve matters.

The correct way would be to test a lot of approaches , and see which approach gives the best result consistently ; I cannot spare the time for this.

Coding-wise , I think the present code does everything correctly , but only repeated trials will tell whether this is so ; however , I think the code is doing a better job now than it was doing earlier.

Try it out and let me know.

Narayan
 

Attachments

  • RealisticData1.xlsm
    44.2 KB · Views: 6
Well done and thank you so much for all your efforts, for what you have done with this issue although it's not completley solved. You have managed to take it way past my expectations of what I thought could be done with the problem. Ultimately though, I have to say if it doesn't work properly then I cannot use it, reasons being real people would be relying on a VBA/program for optainng fair swaps and at this point it would be fair to all involved. I had posted on other forums to and so far no one has managed to take it anywhere near where you have taken it. Many thanks

Code posted below for anyone that might need it for other solutions:

Code:
Public Sub Rearrange_HasWants()
  Dim inputws As Worksheet, outputws As Worksheet
  Dim datarange As Range
  Dim i As Long, j As Long, Numberofrows As Long, rownum As Long, colnum As Long, ChainStartRow As Long, ChainEndRow As Long
  Dim Has As String, Wants As String, Has2 As String, Wants2 As String, EmpID As String, PassedRows As String
  Dim done As Boolean
  Dim rearrangements As Integer
  
  With Me
  Set inputws = .Worksheets("Sheet2")
  Set outputws = .Worksheets("Sheet3")
  End With
  
  outputws.Cells(2, 1).Resize(20, 30).ClearContents
  inputws.Activate
  Set datarange = [HasWants]
  Maxrows = datarange.Rows.Count
  datarange.Offset(, 2).Resize(, 1).ClearContents
'  -------------------------------------------------------------------------------------------------------------------
'  Start with cell A2
'  Initialize i to 1 and Numberofrows to the maximum number of rows
'  Each time a row of data is removed , Numberofrows will be decremented by 1
'  -------------------------------------------------------------------------------------------------------------------

  Columns("A:C").Select
  ActiveWorkbook.Worksheets("Sheet2").Sort.SortFields.Clear
  ActiveWorkbook.Worksheets("Sheet2").Sort.SortFields.Add Key:=Range("B2:B108") _
  , SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
  With ActiveWorkbook.Worksheets("Sheet2").Sort
  .SetRange Range("A1:C108")
  .Header = xlYes
  .MatchCase = False
  .Orientation = xlTopToBottom
  .SortMethod = xlPinYin
  .Apply
  End With
  
  'sort above
  rownum = 2
  colnum = 1
  i = 1
  Numberofrows = Maxrows
  Do While ((Numberofrows > 0) And (i <= Maxrows))
  With datarange
  Has = .Cells(i, 1).Value
  Wants = .Cells(i, 2).Value
  
  If Has = vbNullString Then GoTo LoopExit:
  
'  -----------------------------------------------------------------------------------------------------------
'  If there is data to be processed ,
'  first save the current Has and Wants values in the variables SavedHas and SavedWants
'  We maintain a string variable which keeps track of the rows we have passed while trying to find a match
'  PassedRows is a string variable which will have the row number preceded and followed by an x ,
'  so that there is no accidental match due to two consecutive digits such as 1 and 2 matching row 12
'  as well as rows 1 and 2
'  -----------------------------------------------------------------------------------------------------------
  SavedHas = Has
  SavedWants = Wants
  If .Cells(i, 3) = 1 Then GoTo LoopExit:
  PassedRows = "x" & CStr(i) & "x"
  SavedRows = PassedRows
  .Cells(i, 3).Value = 1
  matchfound = False
  Do
  Swapped = False
  For j = 1 To Maxrows
  If .Cells(j, 3) <> 1 Then
  CurrHas = .Cells(j, 1).Value
  CurrWants = .Cells(j, 2).Value
  If Not IsEmpty(CurrHas) Then
  CurrRow = "x" & CStr(j) & "x"
  If InStr(1, PassedRows, CurrRow) = 0 Then
  If CurrHas = Wants And CurrWants = SavedHas Then
  matchfound = True
  Exit For
  End If
  
  If CurrHas = Wants Then
  Has = CurrHas
  Wants = CurrWants
  Swapped = True
  PassedRows = PassedRows & CurrRow
  Exit For
  End If
  End If
  End If
  End If
  Next
  Loop Until (matchfound Or (Not Swapped))
  
  If matchfound Then
  Has = SavedHas
  Wants = SavedWants
  PassedRows = SavedRows
  
  EmpID = .Offset(, -1).Cells(i, 1).Value
  
  outputws.Cells(rownum, colnum).Value = EmpID & Chr(10) & Has
  outputws.Cells(rownum, colnum + 1).Value = EmpID & Chr(10) & Wants
  colnum = colnum + 2
  
  .Cells(i, 1).Value = vbNullString
  .Cells(i, 2).Value = vbNullString
  .Cells(i, 3) = 1
  Numberofrows = Numberofrows - 1
  Do
  Swapped = False
  For j = 1 To Maxrows
  If .Cells(j, 3) <> 1 Then
  CurrHas = .Cells(j, 1).Value
  CurrWants = .Cells(j, 2).Value
  If Not IsEmpty(CurrHas) Then
  CurrRow = "x" & CStr(j) & "x"
  If InStr(1, PassedRows, CurrRow) = 0 Then
  If CurrHas = Wants And CurrWants = SavedHas Then
  EmpID = .Offset(, -1).Cells(j, 1).Value
  
  outputws.Cells(rownum, colnum).Value = EmpID & Chr(10) & CurrHas
  outputws.Cells(rownum, colnum + 1).Value = EmpID & Chr(10) & CurrWants
  colnum = colnum + 2
  
  .Cells(j, 1).Value = vbNullString
  .Cells(j, 2).Value = vbNullString
  .Cells(j, 3).Value = 1
  Numberofrows = Numberofrows - 1
  Swapped = False
  Exit For
  End If
  
  If CurrHas = Wants Then
  Has = CurrHas
  Wants = CurrWants
  Swapped = True
  PassedRows = PassedRows & CurrRow
  
  EmpID = .Offset(, -1).Cells(j, 1).Value
  
  outputws.Cells(rownum, colnum).Value = EmpID & Chr(10) & Has
  outputws.Cells(rownum, colnum + 1).Value = EmpID & Chr(10) & Wants
  colnum = colnum + 2
  
  .Cells(j, 1).Value = vbNullString
  .Cells(j, 2).Value = vbNullString
  .Cells(j, 3).Value = 1
  Numberofrows = Numberofrows - 1
  Exit For
  End If
  End If
  End If
  End If
  Next
  k = k + 1
  Loop Until ((Not Swapped) Or (k >= Maxrows))
  
  rownum = rownum + 1
  colnum = 1
  i = 1
  Else '
  If Len(PassedRows) > 4 Then
  ChainStartRow = Val(Replace(Left(PassedRows, 4), "x", ""))
  ChainEndRow = Val(Replace(Right(PassedRows, 4), "x", ""))
  .Cells(ChainStartRow, 3).ClearContents
  .Cells(ChainEndRow, 3).Value = 1
  End If
  i = 1
  End If
LoopExit:
  i = i + 1
  End With
  Loop
   datarange.Offset(, 2).Resize(, 1).ClearContents
end sub

Code:
Sub SWAPpart1()
Dim lRow As Long, x1 As Long, varTmp As Variant, tmpArr() As String

lRow = Range("A" & Rows.Count).End(xlUp).Row


With Range("C2:C" & lRow)
  .Formula = Replace("=IFERROR(""A""&AGGREGATE(15,6,ROW($B$2:$B$@)/($A$2:$A$@=B2),COUNTIF(B$2:B2,B2)) & ""-A""&ROW(),"""")", "@", lRow)
  .Value = .Value
End With
End Sub
Code:
Sub SWAPRun()
Dim lRow As Long, x1 As Long, varTmp As Variant, tmpArr() As String

lRow = Range("A" & Rows.Count).End(xlUp).Row


With Range("C2:C" & lRow)
  .Formula = Replace("=IFERROR(""A""&AGGREGATE(15,6,ROW($B$2:$B$@)/($A$2:$A$@=B2),COUNTIF(B$2:B2,B2)) & ""-A""&ROW(),"""")", "@", lRow)
  .Value = .Value
End With

With Range("D2:D" & lRow)
  .Formula = Replace("=IFERROR(""A""&AGGREGATE(15,6,ROW($B$2:$B$@)/($A$2:$A$@=B2),COUNTIF(B$2:B2,B2)) & ""-A""&ROW(),"""")", "@", lRow)
  .Value = .Value
End With


For x1 = 2 To lRow
  If Range("C" & x1).Value = vbNullString Then
  ' NOTHING
  Else
  tmpArr = Split(Range("C" & x1).Value, "-")
  varTmp = Range(tmpArr(0)).Value
  Range(tmpArr(0)).Value = Range(tmpArr(1)).Value
  Range(tmpArr(1)).Value = varTmp
  End If
  Call SWAPpart1
Next x1
End Sub

Code:
Sub GintSwaps(): Dim K, r As Long, i As Long, j As Long
Dim H As String, W As String, p As Integer, q As Integer
p = 5: q = 7: r = Range("A" & Rows.Count).End(xlUp).Row
SimpleSwaps:
For j = 2 To r
If j = 46 Then
j = 46: End If
If Cells(j, p) = "" Then
K = Join(Array(Cells(j, 2), Cells(j, 1)), " ")
For i = 2 To r
If Cells(i, p) = "" Then
If Join(Array(Cells(i, 1), Cells(i, 2)), " ") = K Then
Cells(j, p) = Cells(j, 2)
Cells(i, p) = Cells(i, 2)
Cells(j, p + 1) = i: Cells(i, p + 1) = j
GoTo GetNext: End If: End If: Next i
GetNext: End If: Next j
SingleSwaps: For j = 2 To r: W = Cells(j, 2): H = Cells(j, 1)
If Cells(j, p) = "" Then
For i = 2 To r
If Cells(i, p) = "" Then
If Cells(i, 1) = W Then
Cells(j, p) = W: Cells(i, p) = H
Cells(j, p + 1) = i: Cells(i, p + 1) = j
GoTo Nextj: End If: End If: Next i
Nextj: End If: Next j
DoubleSwaps: For j = 2 To r: W = Cells(j, 2): H = Cells(j, p)
If W <> H Then
For i = 2 To r
If Cells(i, p) = W And Cells(i, q) = "" Then
If Cells(j, q) = "" Then
Cells(j, q) = W: Cells(i, q) = H
Cells(j, q + 1) = i: Cells(i, q + 1) = j
GoTo GoOn: End If: End If: Next i: End If
GoOn: Next j

End Sub
 
Last edited:
Back
Top