How can we dynamically reference past elements in an array without using indices like "i-1", "i-2", and so on?

function play_tune(melody) {
  let played_notes = [];
  let durations = [];
  let positions = [];
  let wait_time = 0;
  let time_passed = 0;

  console.log("Melody initiation.")

  for (let key in melody) {
    played_notes.push(melody[key].note);
    durations.push(melody[key].lasts);
    positions.push(melody[key].starts_at);
  }

  for (let i in positions) {
    if (positions[i] > positions[i - 1]) {
      wait_time = positions[i] - wait_time;
      console.log("Wait " + wait_time + " second(s).");
      console.log("Play " + played_notes[i]);
    } else {
      console.log("Play " + played_notes[i]);
    }

    if (positions[i] == positions[i - 1] && durations[i] == durations[i - 1] || positions[i] + durations[i] == durations[i - 1]) {
      //do nothing.
    } else {
      time_passed += durations[i];
    }

    if ((durations[i] + positions[i]) < time_passed || durations[i] < time_passed) {
      console.log(played_notes[i] + " released.")
    }
  }
  return "Melody ends.";
}

let my_melody = [{
    note: 'C',
    starts_at: 0,
    lasts: 4
  },
  {
    note: 'E',
    starts_at: 0,
    lasts: 4
  },
  {
    note: 'G',
    starts_at: 2,
    lasts: 2
  },
  {
    note: 'G#',
    starts_at: 3,
    lasts: 2
  },
  {
    note: 'Eb',
    starts_at: 3,
    lasts: 2
  }
];

console.log(play_tune(my_melody))

This code is designed to display musical notes in the correct sequence, including when they start, the duration between them, and when to release each note. Each object in the array contains the note name, starting position in seconds, and duration (also in seconds).

It works mostly as expected when looping through each element by index. However, releasing the notes in the correct order proves challenging as I cannot reference notes that appear before the current index. The current implementation is static and needs to be dynamic regardless of the note order.

  if ((durations[i] + positions[i]) < time_passed || durations[i] < time_passed){
     console.log((played_notes[i] + played_notes[i-1] + played_notes[i-2]) + " released.")
  }

When running the code, only the note "G" is released. "C" and "E" are not released. Furthermore, "G#" and "Eb" do not release simultaneously; they play and release consecutively.

Also, the "Wait x second(s)." message before "G#" and "Eb" are played is missing.

The expected console output should be:

Melody initiation.
Play C
Play E
Wait 2 second(s).
Play G
Wait 1 second(s).
Play G#   
Play Eb
C released.
E released.
G released.
Wait 1 second(s).
G# released.
Eb released.
Melody ends.

Due to limitations, advanced features such as foreach loops are not allowed. The solution should be kept as simple as possible using basic constructs like for loops, while loops, if statements, and array and object functions.

Answer №1

To maintain the correct sequence, it is essential to arrange the array based on position first. Subsequently, the previous note will consistently correspond to the one with the preceding index.

function play_soundtrack(melody) {
  let played_notes = [];
  let secs = [];
  let position = [];
  let secs_to_wait = 0;
  let elapsed_time = 0;

  console.log("Melody playback initiated.")

  // Rearrange the melody in ascending order of commencement. Additionally, notes commencing at the same time are ordered by duration.
  melody.sort((a, b) => a.starts_at - b.starts_at || a.lasts - b.lasts);

  for (let key in melody) {
    played_notes.push(melody[key].note);
    secs.push(melody[key].lasts);
    position.push(melody[key].starts_at);
  }

  for (let i in position) {
    if (position[i] > position[i - 1]) {
      secs_to_wait = position[i] - secs_to_wait;
      console.log("Wait for " + secs_to_wait + " second(s).");
      console.log("Playing " + played_notes[i]);
    } else {
      console.log("Playing " + played_notes[i]);
    }

    if (position[i] == position[i - 1] && secs[i] == secs[i - 1] || position[i] + secs[i] == secs[i - 1]) {
      //no action is necessary.
    } else {
      elapsed_time += secs[i];
    }

    if ((secs[i] + position[i]) < elapsed_time || secs[i] < elapsed_time) {
      console.log(played_notes[i] + " release.")
    }
  }
  return "Melody completion.";
}

let my_melody = [{
    note: 'C',
    starts_at: 0,
    lasts: 4
  },
  {
    note: 'E',
    starts_at: 0,
    lasts: 4
  },
  {
    note: 'G',
    starts_at: 2,
    lasts: 2
  },
  {
    note: 'G#',
    starts_at: 3,
    lasts: 2
  },
  {
    note: 'Eb',
    starts_at: 3,
    lasts: 2
  }
];

console.log(play_soundtrack(my_melody))

Answer №2

@Barmar has provided a crucial insight about the necessity of sorting to maintain the order of "starts_at" values. Without proper sorting, the functionality of the code would be compromised, especially if a song is input with notes set incorrectly. It is essential for this answer to closely match the expected output without resorting to i-1 or i-2 references. This exercise has been quite enjoyable, thank you.

function play_music(song) { 
    let elapsedInSeconds = -1; //song running time -1 song havent started yet
    let waitTimeInSeconds = 0; //wait time
    let songLengthInSeconds = -1; //total song length
    let noteToPlayIndex = 0; //note index to play in song

    song.sort((a, b) => {
        //the song total length will be the last note starts and how long it lasts
        const aNoteLengthInSeconds = a.starts_at + a.lasts;
        const bNoteLengthInSeconds = b.starts_at + b.lasts;
        //inject this here so we dont have to waste another loop
        songLengthInSeconds = Math.max(songLengthInSeconds, aNoteLengthInSeconds, bNoteLengthInSeconds);
        //we make sure the last note with longest duration be at the bottom of list
        return a.starts_at - b.starts_at || aNoteLengthInSeconds - bNoteLengthInSeconds; //a.lasts - b.lasts would work too
    });

    console.log('Song started.');
    //play until the end of the song
    while(elapsedInSeconds < songLengthInSeconds) {
        elapsedInSeconds++;
        //when we wait, a second went by so subtract and prevent going below 0
        waitTimeInSeconds = Math.max(0, --waitTimeInSeconds);
        //keep track if this is a waiting tick, we would need to track which keys needed to be released
        let isWaitingTick = waitTimeInSeconds > 0;
        for (let i=0; i < song.length; i++) {
            //if its waiting tick, we don't need to loop through the keys we haven't played
            if (isWaitingTick && i >= noteToPlayIndex) {
                break;
            }
            
            //keep track of the time that we need to release the keys
            const keyReleasedAt = song[i].starts_at + song[i].lasts;

            //doesnt matter if we are in waiting tick or not, if its ready to be release, then release it
            if (keyReleasedAt == elapsedInSeconds) { 
                console.log(song[i].note + " released.");
            }else if (!isWaitingTick) { 
                //play the key if its exact second to play
                if (song[i].starts_at == elapsedInSeconds) {
                    console.log("Play " + song[i].note, song[i].lasts);
                    noteToPlayIndex++;
                //other wise if it needs wait some time, we set our wait timer    
                } else if (song[i].starts_at > elapsedInSeconds) {
                    waitTimeInSeconds = song[i].starts_at - elapsedInSeconds;
                    console.log("Wait " + waitTimeInSeconds + " second(s).");
                    //break so it doesnt look at other notes; other keys would be >= of this note starts_at because of sort
                    break; 
                    
                //if we played all the keys then we should see if we need to wait any time for it to be released
                }  else if ( noteToPlayIndex >= song.length && keyReleasedAt > elapsedInSeconds) {
                    //wait time should be the diff between the second of the song it supposed to be released and current elapse time 
                    waitTimeInSeconds = keyReleasedAt - elapsedInSeconds;
                    console.log("Wait " + waitTimeInSeconds + " second(s).");
                    break;
                }
            }
        }
    }
    return "Song ends.";
}

let my_song = [
    {
        note: 'C',
        starts_at: 0,
        lasts: 4
    },
    {
        note: 'E',
        starts_at: 0,
        lasts: 4
    },
    {
        note: 'G',
        starts_at: 2,
        lasts: 2
    },

    {
        note: 'G#',
        starts_at: 3,
        lasts: 2
    },
    {
        note: 'Eb',
        starts_at: 3,
        lasts: 2
    },
];

console.log(play_music(my_song));

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Difficulty in understanding sentences in the C language

As I work on enhancing my C programming skills, I have come across a perplexing topic concerning pointer declarations. Specifically, I am keen on understanding the distinctions between these two statements: 1. int *ptr[3]; 2. int (*ptr)[3]; Despite thei ...

Tips on improving a function that verifies the existence of an image used as a CSS background to output a boolean value

I have encountered a challenge while working on a react file-upload component. The issue at hand is relatively simple - I aim to display an icon corresponding to the file extension for each uploaded file. These icons are loaded through css as background im ...

Exploring elementary Expressjs query concepts

Just getting started with nodejs and feeling a bit confused. I have a form on my website where users can submit information, and I want to display a response message in an HTML element once the form is submitted. I've been using body parser and Jade v ...

An issue has arisen in the production environment on AWS EC2 due to a problem with Nodemailer

When using nodemailer with node.js to send emails, I have set up the following configuration: var transporter = nodemailer.createTransport({ service: 'gmail', host: 'smtp.gmail.com', auth: { ...

What makes .html effective while innerHTML or appendChild do not have the same impact?

Currently, I am facing a challenge where I am attempting to incorporate an ajax response into a div that contains HTML code involving tables, forms, and more. After testing in Firefox, the innerHTML method worked flawlessly. However, Internet Explorer pre ...

Tips for effectively exchanging information among angular services

Currently, I am in the process of refactoring a complex and extensive form that primarily operates within a controller. To streamline the process, I have started segregating related functions into separate modules or services. However, I am grappling with ...

What is the proper way to transfer data from a file using npm's request package?

I'm currently utilizing npm's request module to interact with an API. The API specifications require that data be sent through a file using the @site.json notation when making a CURL request. For example: curl -X POST -d @site.json 'https ...

I'm surprised by the fact that array.findIndex is not functioning properly. According to Mozilla, it should be working fine

It was necessary to change state.findIndex to state.ids.findIndex in order to resolve the issue I was facing. However, an unexpected behavior occurs when calling the function onClick, as it automatically updates ALL of the active values to true, instead o ...

preserving the current state even after refreshing the page

Currently, I am teaching myself React. When I click on the favorites button, which is represented by a heart symbol, it changes color. However, upon refreshing the page, the color change disappears. To address this issue, I came across the following helpfu ...

I am struggling to figure out the best way to save JSON data retrieved from an API request in a Node.js Express server, and then efficiently send it to a React Native client

My Node.js server is up and running with an app.js file structured like this: var createError = require('http-errors'); var express = require('express'); var path = require('path'); var cookieParser = require('cookie-pars ...

Bytecode representation of arrays and records

In C, I am in the process of developing a brand new programming language and would like to refrain from using third-party code for variable handling. My main concern is how to efficiently represent array assignments, such as apples_in_crate[5] = 170, in b ...

What is the best way to implement forwardRef in a distinct select component?

Currently, I am utilizing react, typescript, and styled-components to work on my project. Specifically, I am aiming to create a select component for use in React Hook Form. Initially, everything seemed to be in order, but I encountered an error from typesc ...

Determine the week number based on a specified starting day for the week

If I have a custom week start day other than Monday, how should the getWeekNumber() prototype for the Date class be modified? Here is the existing code for calculating the ISO Week Number: Date.prototype.getWeekNumber = function() { // Create a dupli ...

Remove every other element from a JSON Array by splicing out the even-numbered items, rather than removing matching items

After receiving a JSON Array Output from a REST API, I am using ng-repeat to display the items on an HTML page. The structure of the received data is as follows: var searchresponse = [{ "items": [{ "employeeId": "ABC", "type": "D", "alive": "Y ...

Retrieving data from Immediately Invoked Function Expressions

I've been working with a closure that looks like this: var Container = (function () { var variable; var changeVariable = function () { variable = 5; }; return { variable: variable, changeVariable: changeVariable }; ...

Working with deeply nested JSON arrays in Java

I have a JSON file in the format shown below. { "data":[ { "prjId": 1, "name" : "Forj1", "issue": [ { "id": 00001, "status" : "Closed" ...

Having issues with AngularJS where ng-table/ng-repeat rows are failing to load properly

I've been following a tutorial on using ng-Table, which can be found Here. When I download the example from Git, it loads without any issues. However, when I try to replicate even the simplest example myself, the column headers and filters load but th ...

What is the best approach to retrieve a username from a SQL database using AJAX within a Flask application

I have been attempting to retrieve the username column information from SQL using ajax, but only the username of the first ID is being returned. However, I need all usernames to be fetched. Below is the model: class users(db.Model): id=db.Column(db.I ...

Tips on including the Elastic Search JavaScript client library in Node.js controller files

Currently, I'm working on a node.js project using the express framework and creating a RESTful API. My next step is to integrate elastic search into my application. I've started by installing the elastic search JavaScript client library and addin ...

What is the best way to add numerous images to a Laravel platform via ajax using pure javascript?

I am currently working on a form where users can upload one or more photos to the server using ajax. Sometimes, users may not upload any photos at all. How can I efficiently send the data from the file input to the server in the background? Below is the r ...