Akin Hwan
Akin Hwan

Reputation: 627

Insert element at Index as Array Lengthens

Lets say I have an array of objects var schedule

Which I am looping through forEach to check whether there is a gap in this schedule for a given day by comparing the current element's end time with the next element's start time. If they are not the same then there is clearly a gap in the schedule.

This all works so far. The issue is in splicing "STUFF" at the correct index. Currently what happens is...for each iteration where I am splicing, the list length is growing by one, and so the [[magical index]] is not currently splicing at the correct index. (Not so magical yet).

var schedule = [{s: "9:00 AM", e: "10:00 AM"}, {s: "11:00 AM", e: "12:00 PM"}, {s: "12:00 PM", e: "2:00 PM"}, {s: "5:00 PM", e: "7:00 PM"}]

schedule.forEach((element, index) => { 
         if(index+1 <= schedule.length -1){
              var nextElement= schedule[index+1]; 
              if( !element.e.isSame(nextElement.s)){
                    //PSEDUO CODE
                    schedule.splice([[magical index]], 0, "STUFF");
              };
         };  
    }

The desired output/result would look like this...with "STUFF"s inserted in the original schedule array var schedule = [{s: "9:00 AM", e: "10:00 AM"}, "STUFF", {s: "11:00 AM", e: "12:00 PM"}, {s: "12:00 PM", e: "2:00 PM"}, "STUFF", {s: "5:00 PM", e: "7:00 PM"}]

Upvotes: 1

Views: 81

Answers (5)

Scott Sauyet
Scott Sauyet

Reputation: 50807

A slightly different approach:

var schedule = [{s: "9:00 AM", e: "10:00 AM"}, {s: "11:00 AM", e: "12:00 PM"},
                {s: "12:00 PM", e: "2:00 PM"}, {s: "5:00 PM", e: "7:00 PM"}]

const insertStuff = sched => sched.reduce((all, curr, idx) => 
  ((idx > 0 && curr.s !== sched[idx - 1].e) ? all.concat('STUFF') : all).concat(curr)
, [])

console.log(insertStuff(schedule))

This one uses much the same technique as Firas Omrane's answer. But it turns it into a reusable function, and uses reduce rather than explicit loops.

I don't know your data, but I can imagine the need for a slight extension, if times can overlap, but you still want to report gaps. This version uses a pretty inelegant timeCompare, but works much like the above:

const timeCompare = (t1, t2) => {
  const [time1, meridian1] = t1.split(/\s/g);
  const [h1, m1] = time1.split(':').map(Number)
  const [time2, meridian2] = t2.split(/\s/g);
  const [h2, m2] = time2.split(':').map(Number)
  return (meridian1 === meridian2) 
     ? (h1 === h2) 
       ? (m1 === m2) ? 0 : (m1 < m2) ? -1 : 1 
       : (h1 < h2) ? -1 : 1 
     : (meridian1 === "AM") ? -1 : 1
}

const overlapped = [{s: "9:00 AM", e: "11:30 AM"}, {s: "11:00 AM", e: "12:00 PM"}, 
                    {s: "1:30 PM", e: "2:00 PM"}, {s: "5:00 PM", e: "7:00 PM"}]

const insertGap = sched => sched.reduce((all, curr, idx) => 
  (idx > 0 && timeCompare(curr.s,  sched[idx - 1].e) > 0) ? all.concat('GAP').concat(curr) : all.concat(curr)
, [])

console.log(insertGap(overlapped))

Upvotes: 1

Jankapunkt
Jankapunkt

Reputation: 8423

Without going too deep into your implementation, I can tell you what has always worked for me with "inserting-while-looping" problems:

loop from end-to-start

When you alter the current index data, you don't need to care about the array's length or successors, which are affected by insert into the current index.

Unfortunately the forEach method does not provide it.

Fortunately it is not hard to write an own one, based on the original:

Array.prototype.forEach2 = function(fun /*, thisp*/) {
    var len = this.length >>> 0;
    if (typeof fun != "function") {
        throw new TypeError();
    }

    var thisp = arguments[1];
    for (var i = len - 1; i >= 0; i--) {
    	fun.call(thisp, this[i], i, this);
    }
};
var schedule = [
	{s: "9:00 AM", e: "10:00 AM"}, 
  {s: "11:00 AM", e: "12:00 PM"}, 
  {s: "12:00 PM", e: "2:00 PM"}, 
  {s: "5:00 PM", e: "7:00 PM"}
];

schedule.forEach2(function(element, index) { 
    var nextElement= schedule[index-1]; 
		if( nextElement && element.s !== nextElement.e){
        schedule.splice(index, 0, "STUFF");
    };
});
    
console.log(schedule);

Edit: note, that you need to inverse the logic inside the function too, otherwise it will not run correctly. However, with this solution you do not introduce a second array variable, which makes it interesting to work with larger sizes.

Upvotes: 1

Kosh
Kosh

Reputation: 18423

You might do it within a simple for loop:

var schedule = [{s: "9:00 AM", e: "10:00 AM"}, {s: "11:00 AM", e: "12:00 PM"}, {s: "12:00 PM", e: "2:00 PM"}, {s: "5:00 PM", e: "7:00 PM"}];

for (let i = 0; i < schedule.length; i++)
  if (schedule[i+1] && schedule[i].e !== schedule[i+1].s)
    schedule.splice(++i, 0, "STUFF");

console.log(schedule);

Upvotes: 1

Firas Omrane
Firas Omrane

Reputation: 932

Very simple and understandable solution. Hope it helps.

var schedule = [{s: "9:00 AM", e: "10:00 AM"}, {s: "11:00 AM", e: "12:00 PM"}, {s: "12:00 PM", e: "2:00 PM"}, {s: "5:00 PM", e: "7:00 PM"}]
var newSchedule =[]; //new array that will contains the result 
const leng = schedule.length ;

schedule.forEach((element, index) => { 
  newSchedule.push(schedule[index]); //always add the schedule elements
  if(index+1 <= leng-1 ){
      var nextElement= schedule[index +1]; 
      if( element.e !== nextElement.s){
            newSchedule.push("STUFF"); //push the string "STUFF" if there is difference.
      };
      
  };  
});

console.log(newSchedule);

Upvotes: 2

Robbie Milejczak
Robbie Milejczak

Reputation: 5780

I would suggest you do this without mutating the array. Splice is one of the worst functions in javascript and is more headache than it's worth. I use this function all the time in production:

function addToArray(arr, item, i) {
  i === 0 ? i = 0 : i = i || arr.length;
  const arr1 = arr.slice(0, i);
  const arr2 = arr.slice(i);
  return arr1.concat([item].concat(arr2));
}

i is an optional argument. If omitted it puts the item on the end, otherwise it puts it in the specified index by slicing up to that index and from that index, and then concatting the arrays together with the desired insert in the middle of the two.

So in your loop, once your condition evals to true you could call the above function with the current index + 1 as your i. If your list was

[{s: "9:00 AM",  e: "10:00 AM"}, 
 {s: "11:00 AM", e: "12:00 PM"}, 
 {s: "12:00 PM", e: "2:00 PM"},
 {s: "5:00 PM",  e: "7:00 PM"}]

and you wanted to add an item after the second item you could just:

schedule = addToArray(schedule, "GAP", index + 1)
//where index is 1

resulting in

[{s: "9:00 AM",  e: "10:00 AM"}, 
 {s: "11:00 AM", e: "12:00 PM"}, 
 "GAP",
 {s: "12:00 PM", e: "2:00 PM"},
 {s: "5:00 PM",  e: "7:00 PM"}]

Hope that makes sense!

Upvotes: 0

Related Questions