Заменить ключи словаря из значений другого словаря

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

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

  1. продолжить, сопоставляя 4-символьные числа, затем 3-символьные числа и т. д.
  2. или продолжите, сопоставляя цифры тысяч, затем сотни цифр и т. д.

Не пытаясь, как бы вы узнали, что лучше? Оказывается, что первый подход (ответ aliteralmind) намного более экономичен.

Ниже я включаю серию тестов на языке PHP, если вы или кто-то еще захотите проверить выход.

Ниже я дам вам регулярное выражение в режиме «свободного пробела», который позволяет комментарии внутри регулярного выражения, чтобы вы могли легко понять, что он делает. Тем не менее, не все двигатели регулярных выражений поддерживают режим свободного пробела, поэтому, прежде чем мы начнем с интересной части, вот регулярное выражение как однострочный.

Обратите внимание, что в вашем вопросе упоминаются числа от -2055 до 2055. Я предположил, что вы хотите сопоставить «нормальные числа», без ведущих нулей. Это означает, что регулярное выражение будет соответствовать 999, но не 0999. Если вы хотите начать нули, дайте мне знать, это очень простая настройка.

Кроме того, если вы согласны в режиме utf-8, \d следует заменить на [0-9]. Это более распространенная форма.

Регулярное выражение как однострочный

^(?!-0$)-?(?:(?=\d{4}$)[12])?(?:(?=\d{3}$)(?:(?:(?<=^)|(?<=^-)|(?<=1)|(?<=-1))\d|(?:(?<=2)|(?<=-2))0))?(?:(?=\d{2}$)(?:(?:(?<=^)|(?<=^-)|(?<=^\d)|(?<=^-\d)|(?<=^\d{2})(?<!20)|(?<=^-\d{2})(?<!-20))\d|(?:(?<=20)|(?<=-20))[0-5]))?(?:(?=\d$)(?:(?:(?<=^)|(?<=^-)|(?<=^\d)|(?<=^-\d)|(?<=^\d{2})|(?<=^-\d{2})|(?<=^\d{3})(?<!205)|(?<=^-\d{3})(?<!-205))\d|(?:(?<=205)|(?<=-205))[0-5]))$

Регулярное выражение в режиме свободного пробега

(?x)                # free-spacing mode
^                   # anchor at beginning of string.
(?!-0$)-?           # optional minus sign, but not for -0

(?:                 # OPTIONAL THOUSANDS DIGIT
(?=\d{4}$)[12]      # assert that the number is 4-digit long, match 1 or 2
)?                  # end optional thousands digit


(?:                 # OPTIONAL HUNDREDS DIGIT
(?=\d{3}$)          # assert that there are three digits left
(?:                 # non-capturing group
  (?:(?<=^)|(?<=^-)|(?<=1)|(?<=-1))\d    # if preceding chars are 1, -1 or head of string: value can be any digit
  |                      # or
  (?:(?<=2)|(?<=-2))0                # if preceding chars are 2 or -2: value must be 0
)                   # close non-capturing group
)?                  # end optional hundreds digits

(?:             # OPTIONAL TENS DIGIT
(?=\d{2}$)      # assert that there are two digits left
(?:             # start non-capturing group

                # if preceding char is head of string, single digit,
                # or two digits that are not 20
                # (with or without a minus)
                # value can be any digit
  (?:(?<=^)|(?<=^-)|(?<=^\d)|(?<=^-\d)|
     (?<=^\d{2})(?<!20)|(?<=^-\d{2})(?<!-20))\d  
|               # or
(?:(?<=20)|(?<=-20))[0-5]  # if preceding chars are 20 or -20: value can be from 0 to 5

)               # end non-capturing group
)?              # close optional tens digits

(?:             # FINAL DIGIT (non optional)
(?=\d$)         # assert that there is only one digit left
(?:             # start non-capturing group

                # if preceding char is head of string, single digit, 
                # two digits, or three digits that are not 205
                # (with or without a minus)
                # value can be any digit
  (?:(?<=^)|(?<=^-)|(?<=^\d)|(?<=^-\d)|
      (?<=^\d{2})|(?<=^-\d{2})|
      (?<=^\d{3})(?<!205)|(?<=^-\d{3})(?<!-205))
      \d
  |             # or
  (?:(?<=205)|(?<=-205))[0-5] # if preceding chars are 205 or -205: value can be from 0 to 5
)               # end non-capturing group
)               # end final digit
$

Серия Тесты

Эти тесты пытаются сопоставлять номера от -100000 до 100000. Они производят следующий вывод:

Successful test: matches from -2055 to 2055
Successful test: NO matches from -100000 to -2056
Successful test: NO matches from 2056 to 100000

Вот код:

<?php
$regex="~^(?!-0$)-?(?:(?=\d{4}$)[12])?(?:(?=\d{3}$)(?:(?:(?<=^)|(?<=^-)|(?<=1)|(?<=-1))\d|(?:(?<=2)|(?<=-2))0))?(?:(?=\d{2}$)(?:(?:(?<=20)|(?<=-20))[0-5]|(?:(?<=^)|(?<=^-)|(?<=^\d)|(?<=^-\d)|(?<=^\d{2})(?<!20)|(?<=^-\d{2})(?<!-20))\d))?(?:(?=\d$)(?:(?:(?<=205)|(?<=-205))[0-5]|(?:(?<=^)|(?<=^-)|(?<=^\d)|(?<=^-\d)|(?<=^\d{2})|(?<=^-\d{2})|(?<=^\d{3})(?<!205)|(?<=^-\d{3})(?<!-205))\d))$~";

// Test 1
$success="Successful test: matches from -2055 to 2055";
for($i=-2055;$i<=2055;$i++) {
$chari = sprintf("%d",$i);
if (! preg_match($regex,$chari)) {
    $success="Failed test: matches from -2055 to 2055";
    echo $chari.": No Match!<br />";
    }
}
echo $success."<br />";

// Test 2
$success="Successful test: NO matches from -100000 to -2056";
for($i=-100000;$i<=-2056;$i++) {
$chari = sprintf("%d",$i);
if (preg_match($regex,$chari)) {
    $success="Failed test: NO matches from -100000 to -2056";
    echo $chari.": Match!<br />";
    }
}
echo $success."<br />";

// Test 3
$success="Successful test: NO matches from 2056 to 100000";
for($i=2056;$i<=100000;$i++) {
$chari = sprintf("%d",$i);
if (preg_match($regex,$chari)) {
    $success="Failed test: NO matches from 2056 to 100000";
    echo $chari.": Match!<br />";
    }
}
echo $success."<br />";

?>

Тесты скорости

Вот результат моего простого теста скорости, соответствующий от -1M до + 1M. Как заметил Казимир, если бы выражение aliteralmind было привязано, вместо медленного, это было бы быстрее на 25%!

zx81: 3.796217918396
aliteralmind: 3.9922280311584
difference: 5.1632998151294 percent longer

Вот тестовый код:

$regex="~(?x)^(?!-0$)-?(?:(?=\d{4}$)[12])?(?:(?=\d{3}$)(?:(?:(?<=^)|(?<=^-)|(?<=1)|(?<=-1))\d|(?:(?<=2)|(?<=-2))0))?(?:(?=\d{2}$)(?:(?:(?<=^)|(?<=^-)|(?<=^\d)|(?<=^-\d)|(?<=^\d{2})(?<!20)|(?<=^-\d{2})(?<!-20))\d|(?:(?<=20)|(?<=-20))[0-5]))?(?:(?=\d$)(?:(?:(?<=^)|(?<=^-)|(?<=^\d)|(?<=^-\d)|(?<=^\d{2})|(?<=^-\d{2})|(?<=^\d{3})(?<!205)|(?<=^-\d{3})(?<!-205))\d|(?:(?<=205)|(?<=-205))[0-5]))$~";

$regex2 = "~(-?\b(?:20(?:5[0-5]|[0-4][0-9])|1[0-9]{3}|[1-9][0-9]{0,2}|(?<!-)0+))\b~";

$start=microtime(TRUE);
for ($i=-1000000;$i<1000000;$i++) preg_match($regex,$i);
$zxend=microtime(TRUE);
for ($i=-1000000;$i<1000000;$i++) preg_match($regex2,$i);
$alitend=microtime(TRUE);
$zx81 = $zxend-$start;
$alit = $alitend-$zxend;
$diff = 100*($alit-$zx81)/$zx81;
echo "zx81: ".$zx81."<br />";
echo "aliteralmind: ".$alit."<br />";
echo "difference: ".$diff." percent longer<br />";
1
задан Stephen Rauch 5 March 2019 в 14:54
поделиться

3 ответа

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

Поскольку вы запросили строковые ключи, вы можете сделать:

from collections import defaultdict

packed_items = {0: [0, 3],
                2: [1], 
                1: [2]}
trucks_dict = {0: [9.5, 5.5, 5.5],
               1: [13.0, 5.5, 7.0],
               2: [16.0, 6.0, 7.0]}
items_dict = {0: [4.6, 4.3, 4.3],
              1: [4.6, 4.3, 4.3],
              2: [6.0, 5.6, 9.0],
              3: [8.75, 5.6, 6.6]}

d = defaultdict(list)

for k1, v1 in trucks_dict.items():
    for k2, v2 in items_dict.items():
        if k1 == k2 % 3:
            d[str(v1)].append(v2)

print(d)
# {'[9.5, 5.5, 5.5]': [[4.6, 4.3, 4.3], [8.75, 5.6, 6.6]], '[16.0, 6.0, 7.0]': [[4.6, 4.3, 4.3]], '[13.0, 5.5, 7.0]': [[6.0, 5.6, 9.0]]}
0
ответ дан Austin 5 March 2019 в 14:54
поделиться

Преобразовав ключи списка в кортежи, вы можете сделать это примерно так:

Код:

result = {}
for k, v in packed_items.items():
    for i in v:
        result.setdefault(tuple(trucks_dict[k]), []).append(items_dict[i])

Код теста:

packed_items = {0: [0, 3],
                2: [1],
                1: [2]}
trucks_dict = {0: [9.5, 5.5, 5.5],
               1: [13.0, 5.5, 7.0],
               2: [16.0, 6.0, 7.0]}
items_dict = {0: [4.6, 4.3, 4.3],
              1: [4.6, 4.3, 4.3],
              2: [6.0, 5.6, 9.0],
              3: [8.75, 5.6, 6.6]}

result = {}
for k, v in packed_items.items():
    for i in v:
        result.setdefault(tuple(trucks_dict[k]), []).append(items_dict[i])
print(result)

Результаты:

{(9.5, 5.5, 5.5): [[4.6, 4.3, 4.3], [8.75, 5.6, 6.6]], 
 (16.0, 6.0, 7.0): [[4.6, 4.3, 4.3]], 
 (13.0, 5.5, 7.0): [[6.0, 5.6, 9.0]]
}
0
ответ дан Stephen Rauch 5 March 2019 в 14:54
поделиться

Вы можете использовать разборчивое понимание, чтобы сопоставить списки в trucks_dict с элементами в items_dict. Списки должны быть преобразованы в кортежи, чтобы их можно было хешировать как ключи:

{tuple(trucks_dict[k]): [items_dict[i] for i in l] for k, l in packed_items.items()}

Возвращает:

{(9.5, 5.5, 5.5): [[4.6, 4.3, 4.3], [8.75, 5.6, 6.6]],
 (13.0, 5.5, 7.0): [[6.0, 5.6, 9.0]],
 (16.0, 6.0, 7.0): [[4.6, 4.3, 4.3]]}
0
ответ дан blhsing 5 March 2019 в 14:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: