4
$\begingroup$

Suppose I have a List of numbers:

num = Range[5]

I want to combine the second and the third element into a sublist to get the result as {1,{2,3},4,5}.
I tried using this:

MapAt[List, num, {{2}, {3}}]

which is not giving me the desired result. What changes are needed to be made?
Can the same changes be applied to this code:

music = SoundNote["CSharp", 0.1, 0.2, "Violin"]

to get the result as SoundNote[CSharp,{0.1,0.2},Violin]?

$\endgroup$
3
  • 2
    $\begingroup$ Given the update with SoundNote, rather than us continuing to throw spaghetti at the wall, it'd be better for you to give a bit more context about how you got to this point. SoundNote has specific semantics (unlike the simple List which you started with), and you're showing us a mal-formed SoundNote. So, it seems that what you're asking for is a way to fix it--make it well-formed. I recommend that you do this with a new function. But you could also fix whatever pre-processing occurred to get you to this point. $\endgroup$
    – lericr
    Sep 1, 2022 at 15:32
  • $\begingroup$ @lericr actually I was trying to convert Pi into music, as asked by me in this question earlier: mathematica.stackexchange.com/questions/272835/… , I tried to add some other instruments in the mix, with some overlap in between which resulted in such mal-formed SoundNote. Thank you for the updated answer. It solved the problem I had :) $\endgroup$
    – codebpr
    Sep 1, 2022 at 15:45
  • $\begingroup$ Possible duplicate of 265101. $\endgroup$
    – Syed
    Sep 11, 2022 at 4:32

5 Answers 5

2
$\begingroup$

For the update with SoundNote, I recommend that you simply write your own "fixing" function:

FixSoundNote[SoundNote[pitch_, start_, end_, style_]] := 
  SoundNote[pitch, {start, end}, style]

Usage:

badMusic = SoundNote["CSharp", 0.1, 0.2, "Violin"];
goodMusic = FixSoundNote[music];
Sound[goodMusic]

You can add other "fix" rules as you discover other malformations that have resulted from the pre-processing.

Having said all of that, it might be better to fix the pre-processing if that's something that you have under your control.

$\endgroup$
4
$\begingroup$

One possibility is to use ReplacePart

Example 1

lst    = Range[5]
idx    = {2, 3};
remove = (# -> Nothing) & /@ idx[[2 ;; -1]]
rep    = First@idx -> lst[[First@idx ;; Last@idx]];

ReplacePart[lst, {rep, Sequence @@ remove}]

Mathematica graphics

Example 2

lst    = Range[10]
idx    = {2, 3, 4};
remove = (# -> Nothing) & /@ idx[[2 ;; -1]];
rep    = First@idx -> lst[[First@idx ;; Last@idx]];

ReplacePart[lst, {rep, Sequence @@ remove}]

Mathematica graphics

This assumes ofcourse that elements to be combined into a list are sequential and have no gaps.

$\endgroup$
4
$\begingroup$

Another possibility;

num = Range[5];
num[[2 ;; 3]] = {num[[2 ;; 3]], Hold@Nothing[]};
num = num // ReleaseHold

(* {1, {2, 3}, 4, 5} *)
$\endgroup$
1
  • 1
    $\begingroup$ Cool approach! Care needs to be taken that the tail of Hold@Nothing needs to be the right length. $\endgroup$
    – lericr
    Sep 1, 2022 at 15:06
3
$\begingroup$

If you want to do this at a position (i.e. you're not looking at individual elements or patterns to determine the nested list), then you'll probably need to break apart the list and reassemble it.

One way:

FlattenAt[TakeList[num, {1, 2, All}], {{1}, {3}}]
(* you'd need to set the list argument in TakeList based on the positions you want grouped together *)

Another way:

Insert[Drop[num, {2, 3}], Take[num, {2, 3}], 2]
(* this way might be more straightforward because the relevant positions are obvious *)

You could also force this into a pattern-matching situation. This could simplify the process if you needed to do this in multiple locations in your list. The way I'll approach this is to create a bespoke "tag" which I'll apply to the target elements and then later use to aggregate them.

SetAttributes[MyTag, Flat];(* This will make it easier to lump elements together *)
SequenceReplace[
  MapAt[MyTag, num, {{2}, {3}}], 
  {tagged__MyTag} -> MyTag[tagged]] /. MyTag -> List

Explanation: First, use MapAt to "tag" each target element--really just wrapping a dummy head around each. Next, use SequenceReplace to wrap another dummy head around any contiguous sequence of "tagged" elements. Since our dummy head has the Flat attribute, this flattens out all of the nested tags. Then we finally just replace the MyTag head with List.

There are many other ways to do this--it'll just depend on your exact circumstance and stylistic preference.

$\endgroup$
1
  • $\begingroup$ I have edited the question. The code doesn't work for the second part of the question(maybe because it's not a list). $\endgroup$
    – codebpr
    Sep 1, 2022 at 15:23
3
$\begingroup$

I guess this is the simplest approach:

num /. {a_, b_, c_, d___} :> {a, {b, c}, d}
 {1, {2, 3}, 4, 5}

At the last position we have BlankNullSequence to make it work.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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