Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I am using the following code to easily generate a row of images of all eight planets of our Solar System:

Labeled[# // Text, AstronomicalData[#, "Image"]] & /@ 
  AstronomicalData["Planet"] // Row

I like the above code because it is concise and gives nice output. However, if I wanted to do something similar for the moons in our Solar System, I run into the problem that only some moons have images. If I run the following line of code, I get a somewhat sloppy output with Missing Image errors embedded.

Labeled[# // Text, AstronomicalData[#, "Image"]] & /@ 
  AstronomicalData["PlanetaryMoon"] // Row

I was wondering if there is a simple way to Map over a list of data conditionally? That is, if an element in a list does not meet a specific condition then skip over it. Note that I'm looking to avoid an explicit loop with an If construct. I really don't mind using a loop with an If statement, but I was curious if there was a concise idiom that anyone could shed some light on.

share|improve this question
2  
This is a beautiful illustration of many ways to solve a problem in Mathematica! –  David Skulsky Feb 3 '12 at 4:52
    
There is a lot of truly excellent feedback on this page. I learned something new from each person's answer. Thank you everyone for contributing. –  Alfred Fazio Feb 3 '12 at 5:06
    
Also, thank you rcollyer for cleaning up my post :) –  Alfred Fazio Feb 3 '12 at 5:07
    
The more I look at these answers the more I see just how much experience you guys have. Seriously a dizzying array of different approaches. I've learned SOO much more looking up the strange code I've seen here than what I originally came here to learn :D Thanks again everyone! –  Alfred Fazio Feb 3 '12 at 5:18
    
You're welcome, and welcome to the community. –  rcollyer Feb 3 '12 at 14:10
add comment

8 Answers 8

up vote 27 down vote accepted

Here's an option which only passes moons with images to the Labeled function:

Labeled[# // Text, AstronomicalData[#, "Image"]] & /@ 
  Select[AstronomicalData["PlanetaryMoon"], 
   ImageQ[AstronomicalData[#, "Image"]] &] // Row

enter image description here

share|improve this answer
    
This was EXACTLY what I was looking for. Your example uses purely functional programming, gets straight to the point, and avoids any unsightly workarounds. Thank you!! –  Alfred Fazio Feb 3 '12 at 5:03
add comment

You will "test" if Missing["NotAvailable"] is returned every time anyway. So a "concise" approach is to filter them out at the end:

Cases[Labeled[# // Text, AstronomicalData[#, "Image"]] & /@ 
   AstronomicalData["PlanetaryMoon"], _[_, _Image]] // Row

I'd be curious to see a more "concise" piece of code. Alternatively instead of using exact pattern _[_, _Image] you could just use _Image and specify all levels in expression to search for that pattern:

Cases[Labeled[# // Text, AstronomicalData[#, "Image"]] & /@ 
   AstronomicalData["PlanetaryMoon"], _Image, Infinity] // Row

But this is a few characters longer ;-)

share|improve this answer
3  
Or complementarily, DeleteCases[(* stuff *), _Missing]... –  J. M. Feb 3 '12 at 4:41
    
Thank you for posting this, Vitaliy. I've learned a lot from your example. I would say that this answer comes in second place only because it requires more computation than the answer provided by David Skulsky. Mr. Skulsky's answer avoids running Map on elements unless they meet the criteria. In your answer all elements are computed and then filtered. Either way, this is a great answer. Thank you! –  Alfred Fazio Feb 3 '12 at 5:24
1  
+1 for Vitaly's answer because his approach only calls AstronomicalData[#,"Image"] once for each moon, whereas my approach calls it twice. –  David Skulsky Feb 3 '12 at 5:32
    
@J.M. I was rather referring to ` _Missing` part - it won't delete because Labeled wrapped around ` _Missing`. You cracked me up with the "idiom" joke ;) –  Vitaliy Kaurov Feb 3 '12 at 5:33
    
Still, the pattern can be changed accordingly. Or, use DeleteCases[] before any further processing's done. :) Anyway, at least you got my intent. –  J. M. Feb 3 '12 at 5:48
add comment

This is absurd in how it looks, but in this case I don't think skipping over the missing images is necessary. Instead, we are going to redefine what Missing means by attaching an upvalue to it using UpSetDelayed (^:=), as follows

Block[{Missing},
  Labeled[_, _Missing] ^:= Sequence[];
  Labeled[# // Text, AstronomicalData[#, "Image"]]
] & /@ AstronomicalData["PlanetaryMoon"] // Row

and returns what you expect. Block allows you to temporarily redefine what a Symbol means, and in this case, I set Missing["NotAvailable"] to return a Sequence[]. An empty sequence has the special property that it will truncate a list it is found in, for example

{a, b, Sequence[], c}

returns

{a, b, c}

So, by redefining Missing to return an empty Sequence, the code shortens the list for you.

Also, here's a purely functional approach that does not require accessing the database more than twice: once for the list of the moons and once for the image.

labelledMoon[name_String, img_?ImageQ] := Labeled[Text@name, img]
labelledMoon[_, _Missing] := Sequence[]
labelledMoon[#, AstronomicalData[#, "Image"]] & /@ 
  AstronomicalData["PlanetaryMoon"] // Row

This employs the feature of an empty Sequence to automatically shorten the list. Here's a self contained version:

With[{name=#, img = AstronomicalData[#, "Image"]},
  If[Head[img]=!=Missing, 
     Labeled[Text@name, img],
     Unevaluated[Sequence[]]
  ]]& /@  AstronomicalData["PlanetaryMoon"] // Row

which gives the same result. Note, this required the use of Unevaluated to ensure that the empty Sequence was inserted properly into the list.

A functional alternative to Unevaluated[Sequence[]] is to use a SlotSequence. As ##&[1,2,3] returns Sequence[1,2,3], then an empty Sequence can be returned via ##&[]. Here's the shortened version:

With[{name=#, img = AstronomicalData[#, "Image"]},
  If[Head[img]=!=Missing, Labeled[Text@name, img], ##&[]]
]& /@  AstronomicalData["PlanetaryMoon"] // Row

All 4 versions of the code output:

enter image description here

share|improve this answer
add comment

In situations like your, where you don't know the number of elements in your result in advance, two functions become very handy: Reap and Sow. The approach is to Sow an element you want in your list deep inside the code and to Reap them at the outside.

To get all primes below 11 you can do

Reap[Table[If[PrimeQ[i], Sow[i]], {i, 10}]]

(*
  {{Null, 2, 3, Null, 5, Null, 7, Null, Null, Null}, {{2, 3, 5, 7}}}
*)

Note that Reap gives you two lists, the first one is your original Table result which contains Null when a number is not prime and the last list contains only the Sowed elements. Therefore, your moons are extracted by

Row[Last[Last[Reap[With[{img = AstronomicalData[#, "Image"]},
       If[Head[img] =!= Missing,
        Sow[Labeled[# // Text, img]]]] & /@ 
     AstronomicalData["PlanetaryMoon"]]]]]
share|improve this answer
add comment

Here is yet another approach:

Cases[
  Thread[ AstronomicalData["PlanetaryMoon", #]& /@ {"Name", "Image"} ],
  {n_, i_Image} :> Labeled[Text@n, i]
] // Row
share|improve this answer
add comment

For shorter lists you can use patterns. For example you want to apply a function only to the even numbers of a list:

n = RandomInteger[{0, 9}, 8]
{5, 2, 1, 6, 8, 5, 7, 7}
n /. i_?EvenQ -> f[i]
{5, f[2], 1, f[6], f[8], 5, 7, 7}

In your case, you would have to replace the EvenQ by something like a HasImageQ, and f by a function that determines how matching entries have to be displayed.

You may also want to have a look at Cases, which is capable of doing a similar thing.

share|improve this answer
add comment

In version 10, the GeneralUtilities context provides a MapIf function that does exactly this:

<<GeneralUtilities`
MapIf[f, EvenQ, Range@10]
(* {1, f[2], 3, f[4], 5, f[6], 7, f[8], 9, f[10]} *)

As with Map, MapIf can also be used in an operator form with MapIf[f, test].

share|improve this answer
add comment

Seems like I'm a year late to this fun filled party, but here's my take:

names = AstronomicalData["PlanetaryMoon"];
data = {names, AstronomicalData[#, "Image"] & /@ names};
MapThread[Labeled[Text@#1, #2] &, data] /. Labeled[_, _Missing] -> Sequence[] // Row
share|improve this answer
    
For what it's worth you don't need pattern names; Labeled[_, _Missing] will work. –  Mr.Wizard Apr 15 '13 at 0:42
    
@Mr.Wizard, Ah yes, since I don't refer to them again. Edited to include your suggestion, thanks. –  RunnyKine Apr 15 '13 at 0:45
    
Also, if you are open to funny looking patterns: DeleteCases[MapThread[Labeled[Text@#, #2] &, data], _~_~_Missing] // Row :-) –  Mr.Wizard Apr 15 '13 at 0:46
    
@Mr.Wizard, you lost me there, you'll have to explain _~_~_Missing to me. –  RunnyKine Apr 15 '13 at 0:48
    
It's equivalent to _[_, _Missing] but in infix form, and without the spacing needed to make that form readable. I don't recommend it in practice! For another humorous pattern see this. Frightening, isn't it? :o) –  Mr.Wizard Apr 15 '13 at 0:51
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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