web-dev-qa-db-fra.com

Comment accéder aux champs de structure de manière dynamique?

J'ai une structure avec beaucoup de champs qui sont des vecteurs de différentes longueurs. Je voudrais accéder aux champs dans une boucle, dans l'ordre. J'ai essayé getfield comme suit, mais MATLAB n'aime pas cela. Comment puis-je faire ceci? 

S = struct('A', [1 2], 'B',[3 4 5]);
SNames = fieldnames(S);
for loopIndex = 1:2
  field = getfield(S, SNames(loopIndex));
  %do stuff w/ field
end
??? Index exceeds matrix dimensions

J'utilise des structures en premier lieu car un tableau aurait des problèmes avec les différentes longueurs de champs. Y a-t-il une meilleure alternative à cela?

31
marciovm

Essayez la référence de champ dynamique où vous mettez une chaîne entre parenthèses, comme indiqué sur la ligne définissant stuff .

S = struct('A', [1 2], 'B',[3 4 5]); 
SNames = fieldnames(S); 
for loopIndex = 1:numel(SNames) 
    stuff = S.(SNames{loopIndex})
end 

Je suis d'accord avec Steve et Adam. Utilisez des cellules. Cette syntaxe convient toutefois aux personnes se trouvant dans d'autres situations!

42
MatlabDoug

Je voudrais faire trois remarques ici:

  • La raison pour laquelle vous obtenez une erreur dans le code ci-dessus est due à la façon dont vous indexez SNames. La fonction fieldnames renvoie un tableau de cellules de chaînes, vous devez donc utiliser indexation du contenu (par exemple, des accolades) pour accéder aux valeurs des chaînes. Si vous modifiez la quatrième ligne de votre code en ceci:

    field = getfield(S, SNames{loopIndex});
    

    alors votre code devrait fonctionner sans erreur.

  • Comme suggéré par MatlabDoug , vous pouvez utiliser les noms de champs dynamiques pour éviter de devoir utiliser getfield (qui produit un code plus propre, à mon avis).

  • La suggestion d'Adam d'utiliser un tableau de cellules au lieu d'une structure est exacte sur la marque. C'est généralement le meilleur moyen de rassembler une série de tableaux de différentes longueurs en une seule variable. Votre code finirait par ressembler à ceci:

    S = {[1 2], [3 4 5]};        % Create the cell array
    for loopIndex = 1:numel(S)   % Loop over the number of cells
      array = S{loopIndex};      % Access the contents of each cell
      % Do stuff with array
    end
    
16
gnovice

L’approche de getfield est acceptable (bien que MATLAB ne soit pas disponible pour le moment et j’ignore pourquoi ce qui précède ne fonctionne pas).

Pour une autre structure de données, vous pouvez également consulter les tableaux de cellules MATLAB. Ils vous permettraient également de stocker et d’indexer des vecteurs de différentes longueurs.

5
Adam Holmberg

Vous pouvez utiliser la notation deux points pour éviter les index:

S = struct('A', [1 2], 'B',[3 4 5]); 
SNames = fieldnames(S); 
for SName = [SNames{:}]
    stuff = S.(SName)
end
3
Niver

Si vous avez besoin d'utiliser une structure, ce que j'ai trouvé très bien fonctionné était de convertir d'abord en cellule, vous avez le meilleur des deux mondes. 

S = struct('A', [1 2], 'B',[3 4 5]); 
S_Cell = struct2cell(S);
%Then as per gnovice
for loopIndex = 1:numel(S_Sell)   % Loop over the number of cells
    array = S{loopIndex};         % Access the contents of each cell
    %# Do stuff with array
end

J'ai utilisé quelque chose de similaire pour quelque chose qui a été généré dans une structure et puis j'ai eu besoin d'y accéder comme une matrice, dans ce cas c'était aussi simple que 

M = cell2mat(struct2cell(S));

Pour le convertir en matrice

2
dabhand

Juste pour ajouter une autre réponse au mélange. J'aime la solution de @Niver, mais cela ne fonctionne que pour les champs avec des noms à une seule lettre. La solution que j'ai utilisée était:

S = struct('A', [1 2], 'B',[3 4 5], 'Cee', [6 7]); 
for SName = fieldnames(S)'
    stuff = S.(SName{1})
end

for va parcourir les colonnes d'un tableau de cellules (d'où la transposition sur fieldnames(S)'. Pour chaque boucle, SName devient un tableau de cellules 1x1. Nous utilisons l'indexation de contenu pour accéder au premier et unique élément avec SName{1}.

0
sclarke81