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 have a nested list similar to this example list (but much bigger and more messy):

str = {{{"a a-a", "a a-b", "a a-c", "a a-d"}, 
        {"a b-a", "a b-b", "a b-c", "a b-d"}}, 
       {{"b a-a", "b a-b", "b a-c", "b a-d"}, 
        {"b b-a", "b b-b", "b b-c", "b b-d"}}};

I am trying to selectively to remove most of the entries whilst keeping the "...-c" entries within the nested structure so that I end up with something like this:

cstr = {{{"a a-c"}, {"a b-c"}}, {{"b b-c"}, {"b b-c"}}};

or at least this:

cstrflat = {{"a a-c", "a b-c"}, {"b b-c", "b b-c"}};

I can find the positions of the desired entries with:

Position[Map[StringMatchQ[#, ___ ~~ "-c"] &, str, {3}], True]

{{1, 1, 3}, {1, 2, 3}, {2, 1, 3}, {2, 2, 3}}

or use Cases to get a flattened list with:

Cases[str, _?(StringMatchQ[#, ___ ~~ "-c"] &), {3}]

{"a a-c", "a b-c", "b a-c", "b b-c"}

but, I can't work out how to thin-out the existing nested list structure. I think it might be possible with Select but my attempts so far haven't worked.

Any suggestions? Are there other ways to do this sort of thing?

share|improve this question
add comment

6 Answers

up vote 6 down vote accepted

You were almost there; you had the criterion right, but all you needed was DeleteCases[]:

DeleteCases[str, s_String /; ! StringMatchQ[s, ___ ~~ "-c"], ∞]
   {{{"a a-c"}, {"a b-c"}}, {{"b a-c"}, {"b b-c"}}}
share|improve this answer
    
This looks like the ticket... and it works on the real dataset right out of the box! I like the use of /;! StringMatchQ.... Many thanks! –  geordie May 30 '13 at 4:47
1  
Note that if your list is very long, this method will be a little faster than mine (on my machine a factor of about 1.2). –  Jonathan Shock May 30 '13 at 5:56
add comment

You can do this with an iterative expression:

takeel[arr_]:=If[Head[arr[[1]]] === List, takeel[#] & /@ arr, Select[arr, StringMatchQ[#, ___ ~~ "-c"] &]]

and then calculate:

takeel[str]

This should give you:

{{{"a a-c"}, {"a b-c"}}, {{"b a-c"}, {"b b-c"}}}

In the comments I was asked to clarify on how the structure takeel[#] & /@ arr works. This is indeed a self-referential function but this is often what is needed when you have a structure and don't know from the outset how deep you will have to go until you can perform your test on some set of elements.

What is happening is that we are asking whether the first element that we are passing in is a list or not. If the first element is not a list then what we have must be a string and we can perform the StringMatchQ on it. If it is a list then we pass the elements of the list one by one back into the function until we have driven down to things which again are not lists.

In fact the way it is written at the moment will not work if we have something like:

{"a a-c", {{"a a-a", "a a-b", "a a-c", "a a-d"}}}

but for the cases you seem to be describing, it should be sufficient.

If the question was about the syntax of function[#]&/@somelist then this is a functional way of passing the elements of a list one by one into the # slot of a function.

share|improve this answer
    
This also works! Can you take a moment to elaborate on how the takeel[#] & /@ arr construct works? It seems to be self-referencing. Many Thanks! –  geordie May 30 '13 at 5:00
1  
I've written a little explanation, please ask if this is not clear. –  Jonathan Shock May 30 '13 at 5:27
    
+1 Great elaboration. Thanks! –  geordie May 30 '13 at 6:19
add comment

I would probably use DeleteCases or Pick:

Pick[str, str, s_String /; StringMatchQ[s, ___ ~~ "-c"]]
{{{"a a-c"}, {"a b-c"}}, {{"b a-c"}, {"b b-c"}}}

Pick appears to be somewhat faster:

big = ConstantArray[str, {40, 40, 40}];

DeleteCases[big, s_String /; ! StringMatchQ[s, ___ ~~ "-c"], ∞] // Timing // First

Pick[big, big, s_String /; StringMatchQ[s, ___ ~~ "-c"]] // Timing // First

1.42

1.17


Exploring other approaches one could write Jonathan's method, without Select, like this:

f[s_String /; StringMatchQ[s, ___ ~~ "-c"]] := s
f[x_List] := f /@ x
f[_] := Sequence[]

f @ str
{{{"a a-c"}, {"a b-c"}}, {{"b a-c"}, {"b b-c"}}}

Or this:

SetAttributes[g, Listable]
g[s_String /; StringMatchQ[s, ___ ~~ "-c"]] := s
g[_] := Sequence[]

g @ str

Or this:

h = Function[, If[Quiet@StringMatchQ[#, ___ ~~ "-c"], #, ## &[]], Listable];
h @ str
share|improve this answer
    
Comprehensive. I like the two overloading methods. Can you briefly explain how the h = ... construct works? Many thanks! –  geordie May 30 '13 at 8:44
1  
@geordie: it works by testing if your strings match the pattern; if they match, they are returned (h becomes the identity function, # &), and if they don't match, they are replaced by a Sequence[] object (the ## &[]), which is effectively equivalent to deletion of the non-matching strings. –  J. M. May 30 '13 at 16:04
add comment

Here is another option:

str/.x_String/;StringFreeQ[x,RegularExpression[".*-c"]]-> Sequence[]

If you specify the level it will have a better performance:

Replace[str,x_String/;StringFreeQ[x,RegularExpression[".*-c"]]-> Sequence[],-1]
share|improve this answer
add comment

I like J.M's answer, but here is another way of approaching it. Find the position in the strings where all the "c"'s are, find the indices,

pos = StringPosition[Flatten[str], "c"]
ind = Flatten[Position[Length /@ pos, 1]]

and then extract from the list:

Flatten[str][[ind]]
{"a a-c", "a b-c", "b a-c", "b b-c"}
share|improve this answer
    
This method fails to maintain the nested structure. –  geordie May 30 '13 at 4:52
add comment

Using your line while negating the predicate:

pos = Position[Map[! StringMatchQ[#, ___ ~~ "-c"] &, str, {-1}], True];

(This achieves the same.)

pos = Position[str, s_String /; ! StringMatchQ[s, ___ ~~ "-c"]];

And deleting elements at those positions:

Delete[str, pos]

{{{"a a-c"}, {"a b-c"}}, {{"b a-c"}, {"b b-c"}}}

I just read this in a book this morning I'm reading (got it right here off this site), it tells that Position and Delete work well as a team. In place of Delete also MapAt, Extract, Insert, ReplacePart among some others I guess (which, anybody?). Great read that book by the way, considering it's some 15 years old, which also proves how coherent Mma stayed over the years and versions I guess.

share|improve this answer
add 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.