# Building a list of products from the elements in another list

I'm new in Mathematica software, and I need help with a simple exercise.

Given the list

list = {x, 3.2, y, 1, 2.679, 3, z, 4, -7.9};


I need to make some rules to generate a new list with only three elements.

• first element is the product of its symbols
• second element the product of its integers
• third element the product of its real numbers

Thus

newlist = {x y z, 12, -67.72512}


I hope you can help me.

Using rules for this task may be not very efficient, but here is a rule-based solution:

list /. {
{List -> Times, _Real   -> 1, _Integer -> 1},
{List -> Times, _Symbol -> 1, _Real    -> 1},
{List -> Times, _Symbol -> 1, _Integer -> 1}
}


{x y z, 12, -67.7251}

You can use GroupBy in several ways:

GroupBy[list, Head, Apply[Times]] /@ {Symbol, Integer, Real}


{x y z, 12, -67.7251}

or using a combination of GroupBy andLookup (thanks: CarlWoll):

Lookup[{Symbol, Integer, Real}] @ GroupBy[list, Head, Apply[Times]]


same result

RotateRight @ Values @ GroupBy[SortBy[Head] @ list, Head, Apply[Times]]


same result

Alternatively, you can use GatherBy:

RotateRight[Times @@@ GatherBy[SortBy[Head] @ list, Head]]


{x y z, 12, -67.7251}

Also, Replace:

Times @@@ Replace[list, {Except @ Blank @ # -> 1} & /@ {Symbol, Integer, Real}, {1}]


and DeleteCases:

Times @@@ (DeleteCases[list, Except@Blank@#] & /@ {Symbol, Integer, Real})

• Lookup would be more idiomatic than mapping: Lookup[GroupBy[list, Head, Apply[Times]], {Symbol, Integer, Real}] – Carl Woll May 1 '19 at 19:53
• Thank you @CarlWoll; great point. – kglr May 1 '19 at 20:00
• Thank you @Shadowray. Hope the updated version works in general. – kglr May 2 '19 at 7:30
• Duplicate symbols may change the head of the product. E.g. {x, x, 1, 2.0} – Shadowray May 2 '19 at 7:38
• Whoops!! Thank you again @Shadowray. Fixed now. – kglr May 2 '19 at 7:47

This doesn't involve any rules, but you could use Cases to select the desired types, using Through and Times as needed:

Times@@@Through@{Cases[_Symbol], Cases[_Integer], Cases[_Real]}@list


{x y z, 12, -67.7251}

A way of doing it with Reap and Sow that would work in versions as old as V5, long before newfangled functions like GroupBy were introduced (V.10).

list = {x, 3.2, y, 1, 2.679, 3, z, 4, -7.9};
Reap[
Switch[#,
_Symbol, Sow[#, "s"],
_Integer, Sow[#, "i"],
_Real, Sow[#, "r"]] & /@ list,
{"s", "i", "r"},
Times @@ #2&][[-1, All, 1]]

{x y z, 12, -67.7251}

• There's no point in using Switch here: Reap[ Sow[#,Head[#]]& /@ list, {Symbol, Integer, Real}, Times@@ #2&][[-1,All,1]] – jjagmath May 3 '19 at 14:02
• @jjagmath. Yes, that is a much better way of doing it. I have updated my answer to include your version. Thanks for bringing it to my attention. – m_goldberg May 3 '19 at 21:12

One approach is to select the elements you want, and then mutliply them.

Times @@@ {Select[list, Not[NumberQ[#]] &],
Select[list, IntegerQ[#] &],
Select[list, NumberQ[#] && Not[IntegerQ[#]] &]}
{x y z, 12, -67.7251}


The first one selects all the symbols (things that are not numbers), the second selects the integers, and the third selects all the reals that are not integers.