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 essentially have two questions, both are related.

1) How do I change a value within a list of lists (I provided a example problem)?

2) Why does my code not work?

I have a list which was created from the Tally function on a text file. The result is shown below...

tally = {{"tree", 5}, {"State", 6}, {"swimming", 3}, {"began", 
2}, {"season", 8}, {"Force", 2}, {"Sooners", 10}, {"three", 
4}, {"second", 14}, {"medley", 6}, {"relay", 17}, {"junior", 
5}, {"sophomore", 3}, {"freshman", 5}, {"coach", 2}, {"Pratt", 
2}, {"think", 2}, {"close", 2}, {"thought", 2}, {"events", 
8}, {"finished", 9}, {"individual", 4}, {"collected", 
4}, {"honors", 4}, {"along", 2}, {"third", 13}, {"showing", 
4}, {"swept", 2}, {"first", 5}, {"Junior", 2}, {"star", 
22}, {"freestyle", 4}, {"runner", 2}, {"Sooner", 2}, {"scored", 
2}, {"respectively", 2}, {"finishes", 4}, {"Sophomore", 
2}, {"opening", 3}, {"finish", 2}, {"lifetime", 2}, {"times", 
2}, {"posted", 3}, {"marks", 2}, {"Championships", 
3}, {"conference", 2}, {"recorded", 2}, {"vital", 2}, {"relays", 
4}, {"school", 3}, {"records", 2}, {"earned", 3}, {"fourth", 
3}, {"campaign", 2}, {"against", 5}, {"Northern", 6}, {"Colorado",
 5}, {"helped", 2}, {"Arizona", 2}, {"Idaho", 2}, {"named", 
2}, {"Academic", 2}, {"backstroke", 2}, {"fifth", 3}, {"regular", 
2}, {"winning", 2}, {"victorious", 2}, {"sixth", 2}, {"RioRancho",
 4}, {"School", 2}};

I want to be able to find the string "star" within the list and change the associated number with that string to 1000. I tried using the following code:

tally /. _?Last@(First@# == "star" &) -> 1000

It does not work the way I want. The number value associated with "star" remains unchanged. I know there is a way to do this. I just can't see it...

Thank you for any assistance!

share|improve this question
7  
Why not just tally /. {"star", _} -> {"star", 1000} ? –  Simon Woods Jan 24 '13 at 20:47
2  
The First part of your pattern works alright, but you can't take the Last of that. It doesn't mean that the last part of your matching is replaced by 1000. –  Sjoerd C. de Vries Jan 24 '13 at 21:22
2  
Simon gave you the solution. Why your version doesn't work is twofold: 1. ? must be followed by a function. First@# == "star" & is a function but (Last@(First@# == "star" &)) is not 2. you are replacing the whole thing that _ ? ... matches not just the second part of that subexpression. –  Szabolcs Jan 24 '13 at 21:31

4 Answers 4

up vote 5 down vote accepted

Here are another 3 options:

If[#[[1]] == "star", {"star", 1000}, #] & /@ tally

But this is a terrible option, I just wrote for pedagogical reasons. Let's see another one:

Replace[tallyBig, {"star", _} -> {"star", 1000}, 1];

This is better, due too level specification, I believe MMA does't waste time looking where it's not necessary. Now the quickest that I get was:

ReplacePart[tallyBig,Position[tallyBig[[All,1]],"star",1]->{"star",1000}]

Let's compare performance:

tallyBig = RandomChoice[tally, 1*^6]

(*Murta1*)   If[#[[1]]=="star",{"star",1000},#]&/@tallyBig;//AbsoluteTiming
(*Szabolcs*) tallyBig/.{"star",_}->{"star",1000};//AbsoluteTiming
(*Murta2*)   Replace[tallyBig,{"star",_}->{"star",1000},1];//AbsoluteTiming
(*Stefan*)   Scan[(tallyBig[[#,2]]=1000)&,Position[tallyBig,"star",{2}][[All,1]]];//AbsoluteTiming
(*Murta3*)   ReplacePart[tallyBig,Position[tallyBig[[All,1]],"star",1]->{"star",1000}];//AbsoluteTiming

{1.294130, Null}
{0.843827, Null}
{0.141805, Null}
{0.133081, Null}
{0.092666, Null}

Now Murta3 is the best option and Murta2 and Stefan gets almost the same result.

share|improve this answer
    
+1 for options, timings, and a fast method. BTW, tallyBig = RandomChoice[tally, 1*^5]; might be a better test list. –  Mr.Wizard Feb 15 '13 at 1:17
    
Tks! I agree, tallyBig updated. :) –  Murta Feb 15 '13 at 1:27
    
Thank you very much for a further analysis of my question. This will be good for a much larger data evaluation and analysis, which is coming soon for me! –  Joseph Feb 15 '13 at 19:45
    
I'm glad to help. Thank you for choosing my answer. :) –  Murta Feb 15 '13 at 21:08

You could also try to get the positions of your string

pos = Position[tally, "star", {2}][[All, 1]]

and then you could use these positions to replace the value.

Scan[(tally[[#, 2]] = 1000) &, pos]

The variable tally is directly modified.

share|improve this answer
    
Nice Scan and Position combination. +1 –  Murta Feb 15 '13 at 0:41

You can use the replacement

tally /. {"star", _} -> {"star", 1000}

Why your version doesn't work is twofold:

  1. ? must be followed by a function. First@# == "star" &is a function but (Last@(First@# == "star" &)) is not

  2. you are replacing the whole thing that _ ? ... matches not just the second part of that subexpression.

share|improve this answer

This answer shows how to define functions that do what is requested.

The first two solutions generate a brand new list. They do not modify the list in place. The third solution modifies the list in place.

Solution 1

replaceViaCopy[lis_, key_, rep_] := 
  ReplacePart[lis, {Position[lis, {key, _}][[1, 1]], 2} -> rep];

test = {{"tree", 5}, {"State", 3000}, {"swimming", 
   3}, {"began", 2}, {"season", 8}, {"Force", 2}, {"Sooners", 
   10}}

replaceViaCopy[test, "State", 6000]

returns

{{"tree", 5}, {"State", 6000}, {"swimming", 3}, {"began", 
  2}, {"season", 8}, {"Force", 2}, {"Sooners", 10}}

Solution 2

This definition is clearer and shorter.

replaceViaCopy2[lis_, key_, rep_] := lis /. {key, _} -> {key, rep}

Solution 3

To change the list without creating a new copy

SetAttributes[replaceInPlace, HoldFirst];

replaceInPlace[lis_, key_, rep_] := 
         (lis[[Position[lis, {key, _}][[1, 1]], 2]] = rep;)

Then

replaceViaCopy[test, "State", 6000]

will not return anything, but will modify test.

share|improve this answer

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.