Каковы некоторые эффективные способы объединения двух структур в MATLAB?

StackOverflow https://stackoverflow.com/questions/38645

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

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

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

Я хотел бы иметь:

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

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

Редактировать: Давайте предположим, что в случае конфликтов имен полей мы отдаем предпочтение A.

Это было полезно?

Решение

Без столкновений вы можете сделать

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 , чтобы изящно перейти к случаю обработки конфликтов.

Другие советы

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


Официальное решение опубликовано Лореном Шуре на ее блог MathWorks, и продемонстрировано с помощью Скфренч здесь и в Ответ Эйтана Т на другой вопрос.Однако, если у вас есть набор инструментов обработки сигналов, простая недокументированная функция уже делает это - 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 цикл, так что это удобная функция с проверкой ошибок и рекурсией для полей, которые сами по себе являются структурами.

Пример

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

% 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' остается неизменным.

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

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

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

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

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

s = catstruct(a,b)

даст

s = 

    f1: 1
    f2: 3
    f3: 4

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

Я использую функцию, которую я написал несколько лет назад, под названием 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

В 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 . По договоренности вы должны отредактировать вопрос, включив в него эту информацию.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top