Многоточие набора имен - PullRequest
       4

Многоточие набора имен

9 голосов
/ 14 февраля 2011

Хорошо, я уверен, что кто-то где-то уже должен был придумать алгоритм для этого, поэтому я решил, что спрошу, прежде чем я сам (заново) изобрел его.

список произвольных (введенных пользователем) непустых текстовых строк.Каждая строка может быть любой длины (кроме 0), и все они уникальны.Я хочу показать их пользователю, но я хочу обрезать их до некоторой фиксированной длины, которую я решу, и заменить часть из них многоточием (...).Подвох в том, что я хочу, чтобы все выходные строки были уникальными.

Например, если у меня есть строки:

  • Microsoft Internet Explorer 6
  • MicrosoftInternet Explorer 7
  • Microsoft Internet Explorer 8
  • Mozilla Firefox 3
  • Mozilla Firefox 4
  • Google Chrome 14

, затемЯ не хотел бы обрезать концы строк, потому что это уникальная часть (не хочу отображать «Microsoft Internet ...» 3 раза), но можно вырезать среднюю часть:

  • Microsoft ... rer 6
  • Microsoft ... rer 7
  • Microsoft ... rer 8
  • Mozilla Firefox 3
  • Mozilla Firefox 4
  • Google Chrome 14

В других случаях средняя часть может быть уникальной, и я хочу обрезать конец:

  • Протокол собрания компании, 25.05.2010 - только для внутреннего использования
  • Протокол собрания компании, 24.06.2010 - только для внутреннего использования
  • Протокол компании Meeting, 23.07.2010 - только для внутреннего использования

может стать:

  • Протокол собрания компании, 25.05.2010 ...
  • протокол собрания компании, 24.06.2010 ...
  • протокол собрания компании, 23.07.2010 ...

Полагаю, он никогда не должен иметь форму эллипса очень начало строк, даже если это было бы иначе разрешено, поскольку это выглядело бы странно.И я предполагаю, что это может увеличить размер нескольких мест в строке, но в пределах разумного - может быть, 2 раза будет в порядке, но 3 или более кажется чрезмерным.Или, может быть, количество раз не так важно, как размер оставшихся кусков: менее чем около 5 символов между эллипсами будет довольно бессмысленным.

Входные данные (и число, и размер) не будутужасно большой, поэтому производительность не является серьезной проблемой (ну, пока алгоритм не пытается что-то глупо, например, перечислить все возможные строки, пока не найдет набор, который работает!).

Я думаю, эти требования кажутсядовольно конкретный, но я на самом деле довольно снисходительный - я просто пытаюсь описать, что я имею в виду.

Было ли что-то подобное сделано раньше?Есть какой-нибудь существующий алгоритм или библиотека, которая делает это?Я немного погуглил, но пока ничего подобного не нашел (но, может быть, я просто плохо гуглю).Я должен верить, что кто-то где-то уже хотел решить эту проблему!

Ответы [ 2 ]

3 голосов
/ 14 февраля 2011

Звучит как приложение самой длинной общей проблемы с подстрокой .

Заменить самую длинную подстроку, общую для всех строк, многоточием. Если строка все еще слишком длинная и вам разрешено делать еще один многоточие, повторите.

Вы должны понимать, что, возможно, вам не удастся «нарезать» заданный набор строк настолько, чтобы соответствовать требованиям длины.

0 голосов
/ 15 февраля 2011

Сортировка строк.Сохраните первые X символов каждой строки.Если этот префикс не уникален для строки до и после, переходите до тех пор, пока не будут найдены уникальные символы (по сравнению со строкой до и после).(Если уникальные символы не найдены, строка не имеет уникальной части, см. Нижнюю часть сообщения.) Добавьте эллипсы до и после этих уникальных символов.

Обратите внимание, что это все равно может выглядеть смешно:

Microsoft Office -> Micro...ffice
Microsoft Outlook -> Micro...utlook

Я не знаю, на каком языке вы собираетесь это делать, но вот реализация Python.

def unique_index(before, current, after, size):
    '''Returns the index of the first part of _current_ of length _size_ that is 
        unique to it, _before_, and _after_. If _current_ has no part unique to it,
        _before_, and _after_, it returns the _size_ letters at the end of _current_'''
    before_unique = False
    after_unique = False
    for i in range(len(current)-size):
        #this will be incorrect in the case mentioned below
        if i > len(before)-1 or before[i] != current[i]:
            before_unique = True
        if i > len(after)-1 or after[i] != current[i]:
            after_unique = True
        if before_unique and after_unique:
            return i

    return len(current)-size

def ellipsize(entries, prefix_size, max_string_length):
    non_prefix_size = max_string_length - prefix_size #-len("...")? Post isn't clear about this.

    #If you want to preserve order then make a copy and make a mapping from the copy to the original
    entries.sort()

    ellipsized = []

    # you could probably remove all this indexing with something out of itertools
    for i in range(len(entries)):
        current = entries[i]

        #entry is already short enough, don't need to truncate
        if len(current) <= max_string_length:
            ellipsized.append(current)
            continue

        #grab empty strings if there's no string before/after
        if i == 0:
            before = ''
        else:
            before = entries[i-1]
        if i == len(entries)-1:
            after = ''
        else:
            after = entries[i+1]

        #Is the prefix unique? If so, we're done.
        current_prefix = entries[i][:prefix_size]    
        if not before.startswith(current_prefix) and not after.startswith(current_prefix):
            ellipsized.append(current[:max_string_length] + '...') #again, possibly -3

        #Otherwise find the unique part after the prefix if it exists.
        else:
            index = prefix_size + unique_index(before[prefix_size:], current[prefix_size:], after[prefix_size:], non_prefix_size)
            if index == prefix_size:
                header = ''
            else:
                header = '...'
            if index + non_prefix_size == len(current):
                trailer = ''
            else:
                trailer = '...'
            ellipsized.append(entries[i][:prefix_size] + header + entries[i][index:index+non_prefix_size] + trailer)
    return ellipsized

Кроме того, вы упоминаете, что сами строки уникальны, но все ли они имеют уникальные части?Например, «Microsoft» и «Microsoft Internet Explorer 7» - это две разные строки, но первая не имеет части, уникальной для второй.Если это так, то вам придется добавить что-то в вашу спецификацию относительно того, что нужно сделать, чтобы сделать этот случай однозначным.(Если вы добавите «Xicrosoft», «MXcrosoft», «MiXrosoft» и т. Д. К смеси с этими двумя строками, будет нет уникальная строка, которая короче исходной строки для представления «Microsoft») (ДругойПодумайте об этом: если у вас есть все возможные X-буквенные строки, вы не можете сжать их все до X-1 или менее строк. Так же, как никакой метод сжатия не может сжимать все входные данные, так как это по сутиметод сжатия.)

Результаты оригинального сообщения:

>>> for entry in ellipsize(["Microsoft Internet Explorer 6", "Microsoft Internet Explorer 7", "Microsoft Internet Explorer 8", "Mozilla Firefox 3", "Mozilla Firefox 4", "Google Chrome 14"], 7, 20):
    print entry

Google Chrome 14
Microso...et Explorer 6
Microso...et Explorer 7
Microso...et Explorer 8
Mozilla Firefox 3
Mozilla Firefox 4
>>> for entry in ellipsize(["Minutes of Company Meeting, 5/25/2010 -- Internal use only", "Minutes of Company Meeting, 6/24/2010 -- Internal use only", "Minutes of Company Meeting, 7/23/2010 -- Internal use only"], 15, 40):
    print entry

Minutes of Comp...5/25/2010 -- Internal use...
Minutes of Comp...6/24/2010 -- Internal use...
Minutes of Comp...7/23/2010 -- Internal use...
...