Совместное появление элементов в коллекциях


У меня есть массив массивов строк, таких как

#(#('smalltalk' 'pharo' 'cool')
  #('smalltalk' 'pharo' 'new')
  #('smalltalk' 'cool'))

И хочу подсчитать совпадения строк в разных коллекциях, поэтому я бы получил следующую информацию:

Smalltalk, pharo, 2
smalltalk, cool, 2
smalltalk, new, 1
pharo, cool, 1
pharo, new, 1
cool, new, 0 (0 случаев необязательны)

Каково было бы наиболее идиоматическое использование методов сбора Smalltalk (Pharo) для сбора этого данные? (Результат может храниться в некотором простом объекте с переменными element1, element2 и count.)

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

2 2

2 ответа:

Я не вижу ничего идиоматического, это относительно сложная операция, я бы разложил на эти операции (код прототипа):

countAssociatedPairsIn aCollection
    | set pairs |
    set := aCollection flattenAllElementsInto: Set new.
    pairs := set generateAllPairs.
    ^pairs collect: [:pair | pair -> (aCollection count: [:associations | associations includesAll: pair])]

Если вы хотите сортировать по количеству, то это просто еще один sortedBy: # value

Если вы просто печатаете, но не собираете:, вы можете превратить некоторые петли в do:

Остается реализовать flattenAllElementsInto: (легко, что-то вроде сплющенного актива), и generateAllPairs (или какой-то allPairsDo: если вы просто печатаете, есть SequenceableCollection>> # комбинации: atATimeDo: что достаточно удобно для этой задачи).

countAssociatedPairsIn aCollection
    | items |
    items:= aCollection flattened asSet asArray sorted.
    items combinations: 2 atATimeDo: [:pair |
        Transcript cr;
            print: pair; nextPutAll: '->';
            print: (aCollection count: [:associations | associations includesAll: pair]);
            flush]

Это дает также обратные случаи, но я с этим согласен.
Он выглядит очень разборчивым для меня, теперь вопрос в том, эффективен ли он:

|source bagOfPairs|

source := #(#('smalltalk' 'pharo' 'cool')
  #('smalltalk' 'pharo' 'new')
  #('smalltalk' 'cool')).

bagOfPairs := Bag new.
source do: [ :each | 
    each allPairsDo: [:first :second |
    (first = second) ifFalse: [bagOfPairs add: {first . second}]
    ].
].
bagOfPairs inspect.