2
$\begingroup$

I have an XML file like this:

xml = XMLObject["Document"][
  XMLElement[
   "whatever", {}, {
     {XMLElement["forename", {"type" -> "first"}, {"Adrian"}], 
      XMLElement["surname", {}, {"Adgar"}]
    },
    {XMLElement["forename", {"type" -> "first"}, {"Babar"}], 
     XMLElement["forename", {"type" -> "middle"}, {"Barbados"}], 
     XMLElement["surname", {}, {"Bode"}]
    }, 
    {XMLElement["forename", {"type" -> "first"}, {"Charly"}], 
     XMLElement["surname", {}, {"Chimp"}]
    }
}]]

I'd like to extract the first names, middle names if any, and surnames.

Using repeated patterns, I would have expected this to work:

Cases[xml, {(XMLElement["forename", _, x_])..., 
      XMLElement["surname", {}, y_]} -> {x, y}, Infinity]
(* {{{"Adrian"}, {"Adgar"}}, {{"Charly"}, {"Chimp"}}} *)

however it misses the middle names. Instead, I would like an output like:

{{"Adrian", "Adgar"}, {"Babar", "Barbados", "Bode"}, {"Charly", "Chimp"}}

I am also wondering what is wrong in this syntax.

$\endgroup$
4
$\begingroup$
Cases[xml,XMLElement["forename" | "surname", _, x_] :> x, \[Infinity]]

(*
{{"Adrian"}, {"Adgar"}, {"Babar"}, {"Barbados"}, {"Bode"}, \
{"Charly"}, {"Chimp"}}
*)

Edit

Looks like we have to guess at what the desired output is so here is guess #2 with first names and surnames grouped. In the example provided first names always precede surnames in the grouping therefore the solution is based on that fact.

Cases[xml, x : {__XMLElement} :> Flatten[x[[All, -1]]], {3}]
(*
{{"Adrian", "Adgar"}, {"Babar", "Barbados", "Bode"}, {"Charly", 
  "Chimp"}}

*)
| improve this answer | |
$\endgroup$
  • $\begingroup$ The problem with that is I don't know if "Adgar" is the middle name of Mr. Babar, or if it's Mr. Adgar. $\endgroup$ – anderstood Feb 22 '18 at 22:24
  • $\begingroup$ @anderstood you need to make it clear in the question what you are seeking. Why not give the output you are after rather than make people guess? $\endgroup$ – Mike Honeychurch Feb 22 '18 at 22:36
3
$\begingroup$

A minimal example showing why the original Cases doesn't work,

data = {{f[1], g[2]}, {f[1], f[2], g[3]}, {f[1], f[1], g[3]}};

Cases[
 data,
 {(f[x_] ..), g[y_]}
 ]
(* {{f[1], g[2]}, {f[1], f[1], g[3]}} *)

Cases[
 data,
 {(f[_] ..), g[y_]}
 ]
(* {{f[1], g[2]}, {f[1], f[2], g[3]}, {f[1], f[1], g[3]}} *)

Using a named pattern alongside Repeated means that the elements that match that named pattern need to be the same. So {f[1], f[2], g[3]} matches {(f[_] ..), g[y_]} but not {(f[x_] ..), g[y_]}.

This loses some brevity, but when in doubt I like to spell my patterns out explicitly.

forenamePattern = XMLElement["forename", _, _];
surnamePattern = XMLElement["surname", _, _];
namePattern = {forenamePattern .., surnamePattern};
getNamesFromPattern = 
  ReplaceAll[x : (forenamePattern | surnamePattern) :> Last@x];
Cases[xml, name : namePattern :> getNamesFromPattern[name], Infinity]
(* {{{"Adrian"}, {"Adgar"}}, {{"Babar"}, {"Barbados"}, \
{"Bode"}}, {{"Charly"}, {"Chimp"}}} *)
| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.