Special order of a list
<p>I have the following problem. It starts with a list which I get. Let's say for example I got the list: <code>A=['1-00', '10--']</code> (The list can be longer or even shorter e.g. <code>B=['01-1', '1-00', '0-01']</code>). It can also have more entries than four or less. Now I first have to sort for the rank. The rank is defined as the number of digits, that you see.</p>

<p>In A I have one string of rank two <code>'10--'</code> and one of rank three <code>'1-00'</code>. (In B I have three of rank three) Now I need to get the number of four binary digits numbers which I get with all of the lowest rank strings first. </p>

<p>Here I have only one rank two string <code>'10--'</code>I can produce: <code>'1000', '1001', '1010', '1011'</code>. So I get <strong>4</strong> binary numbers for rank two. With <code>'1-00'</code> I get <code>'1100'</code> and <code>'1000'</code>. But I already got the second one by the rank two string. So the number I should get is <strong>5</strong> for rank three. That is the number of distinct strings I get with rank two and rank three.</p>

<p>I think its a tough problem but guess there are some commands which I don't know that could help. Hope you can give me some hints :) </p>

<p>If I understand your question, you want to sort by the number of fixed digits ("rank") first, then compute the total number of unique possible bit strings at or below each rank. So for <code>A</code>, you'd want it sorted to <code>['10--', '1-00']</code> (putting lower rank first), and you'd want to get the number of unique patterns for all rank 2 and lower patterns (in this case, there is only one), then for all rank 3 and lower patterns (again, in this case, only one), etc.</p>

<p>If that understanding is correct, the first step is sorting:</p>

<pre><code>A.sort(key=lambda x: x.count('0') + x.count('1'))
# Or if all patterns are of length 4, the slightly simpler:
A.sort(key=lambda x: 4 - x.count('-'))
</code></pre>

<p>After that, you process by ranks using <a href="https://docs.python.org/3/library/itertools.html#itertools.groupby" rel="nofollow"><code>itertools.groupby</code></a>, generating unique outputs with <a href="https://docs.python.org/3/library/itertools.html#itertools.product" rel="nofollow"><code>itertools.product</code></a> and storing them in a <code>set</code> so you remove duplicates:</p>

<pre><code>from __future__ import print_function  # For consistent print on Py2/Py3
from future_builtins import map  # Only on Py2, to get Py3 generator based map

from itertools import groupby, product

uniquebits = set()
for rank, pats in groupby(A, key=lambda x: x.count('0') + x.count('1')):
    for pat in pats:
        # product can be abused to sub 0, then 1 for each wildcarded space
        patpieces = [let if let != '-' else '01' for let in pat]
        # Generate all patterns (using ''.join reduces storage by converting
        # tuples back to str) updating the set to get unique values seen so far
        uniquebits.update(map(''.join, product(*patpieces)))
    print("For rank", rank, "and below,", len(uniquebits), "unique bit strings")
</code></pre>

<p>Which for your <code>A</code> would output:</p>

<pre class="lang-none prettyprint-override"><code>For rank 2 and below, 4 unique bit strings
For rank 3 and below, 5 unique bit strings
</code></pre>

<p>If you want to avoid gaps, and output for all ranks out to the maximum length of the patterns, it's slightly more complicated (since <code>groupby</code> only produces groups when there is at least one input with that "key", in this case, at least one input of a given rank):</p>

<pre><code>lastrank = None  # Change to 1 or 2 to force ranks prior to lowest rank to output
for rank, pats in groupby(A, key=lambda x: x.count('0') + x.count('1')):
    if lastrank is not None and lastrank + 1 != rank:
        for fillrank in range(lastrank + 1, rank):
            print("For rank", fillrank, "and below,", len(uniquebits), "unique bit strings")
    lastrank = rank

    for pat in pats:
        # product can be abused to sub 0, then 1 for each wildcarded space
        patpieces = [let if let != '-' else '01' for let in pat]
        # Generate all patterns (using ''.join reduces storage by converting
        # tuples back to str) updating the set to get unique values seen so far
        uniquebits.update(map(''.join, product(*patpieces)))
    print("For rank", rank, "and below,", len(uniquebits), "unique bit strings")

# Or len(max(A, key=len))+1 if uneven lengths
for rank in range(lastrank + 1, len(A[0]) + 1):
    print("For rank", rank, "and below,", len(uniquebits), "unique bit strings")
</code></pre>

python list