Is there a way to control which special forms Normal converts?

Normal[expr] converts expr to a normal expression, from a variety of special forms.

But is there a way to control which special forms it converts and which it does not?

Yes.

It turns out that Normal accepts an undocumented second parameter which may be a Symbol or a list of Symbols and it will only affect those forms. Plain application converts all these forms (and more):

abby = {
SparseArray[{3 -> "a", 5 -> "b"}],
<|1 -> "a", 2 -> "b", 3 -> "c"|>,
Series[Exp[x], {x, 0, 5}],
Quantity[1, "Percent"]
};

abby // Normal
{{0, 0, "a", 0, "b"},
{1 -> "a", 2 -> "b", 3 -> "c"},
1 + x + x^2/2 + x^3/6 + x^4/24 + x^5/120,
1/100}

We can target the specific forms like this:

abby ~Normal~ SparseArray
abby ~Normal~ Quantity
{{0, 0, "a", 0, "b"},
<|1 -> "a", 2 -> "b", 3 -> "c"|>,
SeriesData[x, 0, {1, 1, 1/2, 1/6, 1/24, 1/120}, 0, 6, 1],
Quantity[1, "Percent"]}

{SparseArray[< 2 >, {5}],
<|1 -> "a", 2 -> "b", 3 -> "c"|>,
SeriesData[x, 0, {1, 1, 1/2, 1/6, 1/24, 1/120}, 0, 6, 1],
1/100}

Or multiple types with:

abby ~Normal~ {SeriesData, Association}
{SparseArray[< 2 >, {5}],
{1 -> "a", 2 -> "b", 3 -> "c"},
1 + x + x^2/2 + x^3/6 + x^4/24 + x^5/120,
Quantity[1, "Percent"]}

Usage note and correction

Previously I wrote that Normal passes over held expressions. It does not.

Some cases could be handled equivalently with a simple replacement like:

expr /. sa_SparseArray :> Normal[sa]

However evaluation is not equivalent such as within a held expression:

held = Hold @@ abby

held /. sa_SparseArray :> Normal[sa]

held ~Normal~ SparseArray
Hold[
Normal[SparseArray[< 2 >, {5}]],
<|1 -> "a", 2 -> "b", 3 -> "c"|>,
SeriesData[x, 0, {1, 1, 1/2, 1/6, 1/24, 1/120}, 0, 6, 1],
Quantity[1, "Percent"]
]

Hold[
{0, 0, "a", 0, "b"},
<|1 -> "a", 2 -> "b", 3 -> "c"|>,
SeriesData[x, 0, {1, 1, 1/2, 1/6, 1/24, 1/120}, 0, 6, 1],
Quantity[1, "Percent"]
]
• Igor approves. +1 – ciao May 30 '15 at 5:39
• MapAt[Normal,abby,1]? Just kidding, I can see the potential if you don't know what kind of data types will be returned and you want to get rid of a specific one (Quantity would be my target, I guess). – Sjoerd C. de Vries May 30 '15 at 8:38
• @Sjoerd To catch expressions at all levels one would need something like expr /. q_Quantity :> Normal[q] which is fine but a bit less clean. There is also a difference in held expressions, e.g. Hold[Quantity[5, "Percent"]] would return Hold[Normal[Quantity[5, "Percent"]]] with the rule above while Normal would simply pass over that expression; one would need to add a skip rule like /. {x : _Hold | _HoldForm | _HoldComplete :> x, q_Quantity :> Normal[q]} to approach that behavior and it would still miss user functions with a hold attribute so that would require further testing. – Mr.Wizard May 30 '15 at 15:42
• As of version 10.x, this feature appears to be documented – QuantumDot Dec 23 '16 at 19:38
• @QuantumDot Thanks. That is not in the version 10.1.0 documentation. I wonder when it was added. – Mr.Wizard Dec 23 '16 at 19:54