I was looking for an algorithm for sorting an array in a custom way but I didn't succeed in finding the proper solution to my problem. I'll describe the code in Django-like syntax but it's not necessary to limit a solution only for Django.
Let's suppose I have the following models (classes):
class Website(models.Model):
...
class Offer(models.Model):
website = models.ForeignKey(Website, on_delete=models.CASCADE)
...
And let's suppose I have the following instances:
- Offer 1 -> Website A
- Offer 2 -> Website B
- Offer 3 -> Website B
- Offer 4 -> Website B
- Offer 5 -> Website C
- Offer 6 -> Website A
- Offer 7 -> Website A
- Offer 8 -> Website C
This instances form a sequence (array):
sequence = [Offer 1, Offer 2, Offer 3, Offer 4, Offer 5, Offer 6, Offer 7, Offer 8]
I need to sort the sequence in the way where Offers with the same Website cannot stand one after another nev开发者_运维技巧ertheless the original order should stay as same as possible.
So the sorted sequence should look this way:
sequence = [Offer 1, Offer 2, Offer 5, Offer 3, Offer 6, Offer 4, Offer 7, Offer 8]
Positive Examples:
- Website A, Website B, Website A, Website C, Website A
- Website A, Website B, Website C, Website B, Website C
- Website A, Website B, Website A, Website B, Website A
Negative Examples:
- Website A, Website B, Website B, Website A, Website B, ...
- Website B, Website C, Website A, Website A, Website B, ...
- Website B, Website C, Website A, Website C, Website C, ...
Thanks for any suggestion.
Try this:
def sort_custom(offers):
sorted_offers, sorted_count, index = [], len(offers), 0
while sorted_count > 0:
item = offers[index]
if not sorted_offers or sorted_offers[-1] != item:
sorted_offers.append(item)
sorted_count -= 1
del offers[index]
if index > 0: index = 0
else:
if index < len(offers) - 1:
index += 1
else:
sorted_offers += offers
break
return sorted_offers
Usage:
>> lst = ['A', 'B', 'B', 'B', 'C', 'A', 'A', 'C']
>> sort_custom(lst)
['A', 'B', 'C', 'B', 'A', 'B', 'A', 'C']
>> lst2 = ['C', 'A', 'C', 'A', 'C', 'A', 'A', 'A']
>> sort_custom(lst2)
['C', 'A', 'C', 'A', 'C', 'A', 'A', 'A']
timing:
>> # for lst = ['A', 'B', 'B', 'B', 'C', 'A', 'A', 'C']
>> timer.repeat(3, 2000000)
[0.4880218505859375, 0.4770481586456299, 0.4776880741119385]
This should work:
def gen_best_order(orig):
last = None
while len(orig) > 0:
deli = None
for i, m in enumerate(orig):
if m.website != last.website:
last = m
deli = i
yield m
break
if deli is None:
last = orig[0]
yield orig[0]
deli = 0
del orig[deli]
ordered = list(gen_best_order(sequence))
This is a generator that will try and yield elements in order, but if the next element equals the last element yielded, it will skip it. If it gets to the end of the list and there is no way to yield something that doesn't equal the previous, it just yields it anyway.
Here's an example of it working on a list of numbers:
def gen_best_order(orig):
last = None
while len(orig) > 0:
deli = None
for i, m in enumerate(orig):
if m != last:
last = m
deli = i
yield m
break
if deli is None:
last = orig[0]
yield orig[0]
deli = 0
del orig[deli]
nums = [1,2,3,3,4,5,5]
print 'orig:', nums
print 'reordered:', list(gen_best_order(nums))
This prints:
orig: [1, 2, 3, 3, 4, 5, 5]
reordered: [1, 2, 3, 4, 3, 5, 5]
精彩评论