Каковы эффективные способы объединения двух структур в MATLAB? - PullRequest
23 голосов
/ 02 сентября 2008

Я хочу объединить две структуры с разными именами полей.

Например, начиная с:

A.field1 = 1;
A.field2 = 'a';

B.field3 = 2;
B.field4 = 'b';

Хотелось бы иметь:

C.field1 = 1;
C.field2 = 'a';
C.field3 = 2;
C.field4 = 'b';

Есть ли более эффективный способ, чем использование "fieldnames" и цикла for?

РЕДАКТИРОВАТЬ: Предположим, что в случае конфликтов имен полей мы отдаем предпочтение A.

Ответы [ 5 ]

18 голосов
/ 02 сентября 2008

Без столкновений можно обойтись

M = [fieldnames(A)' fieldnames(B)'; struct2cell(A)' struct2cell(B)'];
C=struct(M{:});

И это достаточно эффективно. Однако struct ошибки на повторяющихся именах полей и предварительная проверка их с использованием unique снижают производительность до такой степени, что цикл становится лучше. Но вот как это будет выглядеть:

M = [fieldnames(A)' fieldnames(B)'; struct2cell(A)' struct2cell(B)'];

[tmp, rows] = unique(M(1,:), 'last');
M=M(:, rows);

C=struct(M{:});

Возможно, вам удастся создать гибридное решение, не допуская конфликтов и используя команду try / catch для вызова struct для постепенного перехода к случаю обработки конфликта.

8 голосов
/ 20 мая 2014

Краткий ответ: setstructfields (если у вас есть набор инструментов для обработки сигналов).


Официальное решение опубликовано Лорен Шуре на ее блоге MathWorks и продемонстрировано SCFrench здесь и ответом Эйтана Т на другой вопрос . Однако, если у вас есть набор инструментов для обработки сигналов, простая недокументированная функция уже делает это - setstructfields.

help setstructfields

 setstructfields Set fields of a structure using another structure
    setstructfields(STRUCTIN, NEWFIELDS) Set fields of STRUCTIN using
    another structure NEWFIELDS fields.  If fields exist in STRUCTIN
    but not in NEWFIELDS, they will not be changed.

Внутренне он использует цикл fieldnames и for, поэтому это удобная функция с проверкой ошибок и рекурсией для полей, которые сами являются структурами.

* * Пример тысячи двадцать-шести * 1 028 *

"Оригинальная" структура:

% struct with fields 'color' and 'count'
s = struct('color','orange','count',2)

s = 
    color: 'orange'
    count: 2

Вторая структура, содержащая новое значение для 'count' и новое поле, 'shape':

% struct with fields 'count' and 'shape'
s2 = struct('count',4,'shape','round')

s2 = 
    count: 4
    shape: 'round'

Звонок setstructfields:

>> s = setstructfields(s,s2)
s = 
    color: 'orange'
    count: 4
    shape: 'round'

Поле 'count' равно обновлено . Поле 'shape' равно добавлено . Поле 'color' остается без изменений .

ПРИМЕЧАНИЕ : поскольку эта функция недокументирована, она может быть изменена или удалена в любое время.

5 голосов
/ 19 сентября 2013

Я нашел хорошее решение для обмена файлами: catstruct .

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

Вот как это работает:

a.f1 = 1;
a.f2 = 2;
b.f2 = 3;
b.f4 = 4;

s = catstruct(a,b)

даст

s = 

    f1: 1
    f2: 3
    f3: 4
4 голосов
/ 10 декабря 2008

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

Я использую функцию, которую я написал несколько лет назад, под названием setdefaults.m, которая объединяет одну структуру со значениями другой структуры, где одна имеет приоритет над другой в случае конфликта.

% SETDEFAULTS sets the default structure values 
%    SOUT = SETDEFAULTS(S, SDEF) reproduces in S 
%    all the structure fields, and their values,  that exist in 
%    SDEF that do not exist in S. 
%    SOUT = SETDEFAULTS(S, SDEF, OVERRIDE) does
%    the same function as above, but if OVERRIDE is 1,
%    it copies all fields of SDEF to SOUT.

function sout = setdefaults(s,sdef,override)
if (not(exist('override','var')))
    override = 0;
end

sout = s;
for f = fieldnames(sdef)'
    cf = char(f);
    if (override | not(isfield(sout,cf)))
        sout = setfield(sout,cf,getfield(sdef,cf));
    end
end

Теперь, когда я думаю об этом, я почти уверен, что ввод "переопределения" не нужен (вы можете просто изменить порядок входов), хотя я не уверен на 100% в этом ... так вот проще переписать (setdefaults2.m):

% SETDEFAULTS2 sets the default structure values 
%    SOUT = SETDEFAULTS(S, SDEF) reproduces in S 
%    all the structure fields, and their values,  that exist in 
%    SDEF that do not exist in S. 

function sout = setdefaults2(s,sdef)
sout = sdef;
for f = fieldnames(s)'
    sout = setfield(sout,f{1},getfield(s,f{1}));
end

и некоторые образцы для тестирования:

>> S1 = struct('a',1,'b',2,'c',3);
>> S2 = struct('b',4,'c',5,'d',6);
>> setdefaults2(S1,S2)

ans = 

    b: 2
    c: 3
    d: 6
    a: 1

>> setdefaults2(S2,S1)

ans = 

    a: 1
    b: 4
    c: 5
    d: 6
2 голосов
/ 02 сентября 2008

В C структура может иметь другую структуру в качестве одного из своих членов. Хотя это не совсем то, что вы просите, вы можете столкнуться с ситуацией, когда одна структура содержит другую, или одна структура содержит две структуры, каждая из которых содержит части информации, которую вы хотели. *

psuedocode: я не помню фактический синтаксис.

A.field1 = 1;
A.field2 = 'a';
A.field3 = struct B;

для доступа: A.field3.field4;

или что-то в этом роде.

Или вы можете иметь структуру C, содержащую как A, так и B:

C.A = struct A;
C.B = struct B;

с доступом потом что-то вроде

C.A.field1;
C.A.field2;
C.B.field3;
C.B.field4;

надеюсь, это поможет!

РЕДАКТИРОВАТЬ: оба эти решения позволяют избежать конфликтов имен.

Кроме того, я не увидел ваш тег matlab. По договоренности вам следует отредактировать вопрос, включив в него эту информацию.

...