Функция разделения строк может принимать опцию логического аргумента «expand».
Вот решение, использующее этот аргумент:
a.var1.str.split(",",expand=True).set_index(a.var2).stack().reset_index(level=1, drop=True).reset_index().rename(columns={0:"var1"})
Я согласен с комментарием I82. То, что вам следует избегать ссылки B на родителя: это дублирование информации, которое обычно приводит только к неприятностям, но вам может понадобиться сделать это в вашем случае.
если вы оставите родительскую ссылку в B
, в отношении хэш-кодов вы должны полностью игнорировать родительскую ссылку и использовать внутренние переменные true B
для построения хеш-кода.
A
s - это только контейнеры, и их значение полностью определяется их содержимым, которое является значениями содержащихся B
s, и поэтому должны иметь свои хэш-ключи.
Если A
является неупорядоченным множеством, вы должны быть очень осторожны, чтобы хэш-код, который вы строили из значений B
(или [х7 хэш-коды]), не зависит от какого-либо заказа. Например, если хэш-код создается путем добавления и умножения хеш-кодов содержащихся B
в некоторой последовательности, вы должны сначала заказать хеш-коды, увеличив порядок перед вычислением результата сумм / умножений. Аналогично, A.equals(o)
не должен зависеть от порядка B
s (если неупорядоченный набор).
Обратите внимание, что если вы используете java.util.Collection
внутри A
, просто установите B
s, игнорируя родительскую ссылку, автоматически выдаст действительные хеш-коды A
, поскольку по умолчанию Collection
имеют хорошие хеш-коды (упорядочение или нет).
Прежде всего, вы уверены, что хотите переопределить Equals()
и GetHashCode()
? В большинстве сценариев вы должны быть в порядке с ссылочным равенством по умолчанию.
Но, допустим, нет. Чем, какова соответствующая семантика равенства, которую вы хотите?
Например, предположим, что каждый A
имеет поле getB
типа B
, и каждый B
имеет поле getA
типа A
. Пусть a1
и a2
- два объекта A
, имеют одинаковые поля и одинаковые getB
(такие же, как в «том же адресе памяти») b1
. Являются ли a1
и a2
равными? Предположим, что b1.getA
совпадает с a1
(то же, что и в «том же адресе памяти»), но не совпадает с a2
. Вы все еще хотите считать a1
и a2
равными?
Если нет, не переопределяйте что-либо и не используйте ссылочное равенство по умолчанию.
Если да, то вот решение: пусть A
имеет функцию int GetCoreHashCode()
, которая не зависит от элемента getB
(но зависит от других полей). Пусть функция B
имеет функцию int GetCoreHashCode()
, которая не зависит от элемента getA (но зависит от других полей). Пусть теперь функция int GetHashCode()
A
зависит от this.GetCoreHashCode()
и getB.GetCoreHashCode()
, а также для B
, и вы закончили.
В типичной модели большинство объектов имеют уникальный идентификатор. Этот идентификатор полезен в различных случаях использования (в частности: восстановление базы данных / поиск). IIUC, поле bKey должно быть таким уникальным идентификатором. Таким образом, общая практика сравнения таких объектов заключается в сравнении их идентификаторов:
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (!getClass().equals(obj.getClass()))
return false;
return this.bKey.equals(((B) obj).bKey);
}
@Override
public int hashCode() { return bKey.hashCode(); }
Вы можете спросить: «что произойдет, если два объекта B имеют одинаковый идентификатор, но другое состояние (значение их полей отличается )». Ваш код должен убедиться, что таких вещей не происходит. Это будет проблемой, независимо от того, как вы реализуете equals()
или hashCode()
, потому что это по существу означает, что у вас есть две разные версии одного и того же объекта в вашей системе, и вы не сможете определить, что является правильным.
Вот несколько библиотек для этой цели:
https://code.google.com/p/deep-equals/
У вас может быть два варианта equals
- переопределение Object.equals
и одно, которое лучше подходит для рекурсии. Рекурсивная проверка равенства принимает A или B - в зависимости от того, какой класс другого этого объекта - это объект, который вы называете рекурсивным равенством от имени. Если вы вызываете его от имени this.equals
, вы проходите в null
. Например:
A {
...
@Override
public boolean equals(Object obj) {
// check for this, null, instanceof...
A other = (A) obj;
return recursiveEquality(other, null);
}
// package-private, optionally-recursive equality
boolean recursiveEquality(A other, B onBehalfOf) {
if (onBehalfOf != null) {
assert b != onBehalfOf;
// we got here from within a B.equals(..) call, so we just need
// to check that our B is the same as the one that called us.
}
// At this point, we got called from A.equals(Object). So,
// need to recurse.
else if (b == null) {
if (other.b != null)
return false;
}
// B has a similar structure. Call its recursive-aware equality,
// passing in this for the onBehalfOf
else if (!b.recursiveEquality(other.b, this))
return false;
// check bkey and return
}
}
Итак, после A.equals
:
A.equals
вызывает `recursiveEquality (otherA, null), если this.b != null
, мы закончили третий блок if-else, который вызывает b.recursiveEquality(other.b, this)
в B.recursiveEquality
, мы попали в блок first if-else, который просто утверждает, что наш A
является тем же, который был передан нам (т. е. что круговая ссылка не нарушена), мы заканчиваем B.recursiveEquality
, проверяя aKey
(в зависимости от ваших инвариантов вы можете утверждать что-то, основанное на том, что произошло на шаге 3). B.recursiveEquality
возвращает A.recursiveEquality
, проверяя bKey
, возможно, с аналогичными утверждениями A.equals
возвращает результат проверки рекурсивного равенства