最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Compare each element in an array against every other element - Stack Overflow

programmeradmin1浏览0评论

I need to pare schedules for overlap, anywhere from 2 to infinite number of schedules.

For example, an array with 3 schedules would look like this:

var dateRanges =  [
    {
      DaysOfWeek: ['Sun', 'Mon'],
      StartTime: "01:00",
      StopTime: "17:00",
      AllDay: false
    },
    {
      DaysOfWeek: ['Tues', 'Wed'],
      StartTime: "12:00",
      StopTime: "21:59",
      AllDay: true
    },
            {
      DaysOfWeek: ['Thur', 'Sun'],
      StartTime: "12:00",
      StopTime: "21:59",
      AllDay: true
    }
  ]

I'm struggling to figure out how to pare all arrays with each other. So far I have this

checkScheduleForOverlap: function (dateRanges) {

  var result = dateRanges.reduce((result, current, i, arr) => {
    // console.log(previous, current);

    // get the previous range
    if (i === 0) { return result; }
    var previous = arr[i - 1];

    // Schedule1
    var startTime1 = new Date('1970-01-01T' + previous.StartTime + 'Z');
    var stopTime1 = new Date('1970-01-01T' + previous.StopTime + 'Z');

    // Schedule2
    var startTime2 = new Date('1970-01-01T' + current.StartTime + 'Z');
    var stopTime2 = new Date('1970-01-01T' + current.StopTime + 'Z');

    previous.DaysOfWeek.forEach(function (prevDay) {

      console.log(prevDay);
      current.DaysOfWeek.forEach(function (currDay) {
        console.log(currDay);

        if (prevDay === currDay) {
          var overlap = (startTime1 <= stopTime2) && (stopTime1 >= startTime2);

          // store the result
          if (overlap) {
            // yes, there is overlap
            result.overlap = true;
            // store the specific ranges that overlap
            result.days.push(currDay);
          }
        }
      });
    });

    return result;

    // seed the reduce
  }, { overlap: false, days: [] });

  // return the final results
  console.log(result);
  return result;

}

But it only pares the second array with the first, and the third with the second. It also needs to pare the third with the first. (and if there were 4 schedules, each would need to pare to the other.)

Am I on the right track here, and what can be done to get each DaysOfWeek schedule to pare the StartTime and StopTime with the values in the other schedule?

I created a fake date object using a static day, and I'm only paring the time values.

I'm open to doing it a pletely different way if this is not an efficient way to do it.

I need to pare schedules for overlap, anywhere from 2 to infinite number of schedules.

For example, an array with 3 schedules would look like this:

var dateRanges =  [
    {
      DaysOfWeek: ['Sun', 'Mon'],
      StartTime: "01:00",
      StopTime: "17:00",
      AllDay: false
    },
    {
      DaysOfWeek: ['Tues', 'Wed'],
      StartTime: "12:00",
      StopTime: "21:59",
      AllDay: true
    },
            {
      DaysOfWeek: ['Thur', 'Sun'],
      StartTime: "12:00",
      StopTime: "21:59",
      AllDay: true
    }
  ]

I'm struggling to figure out how to pare all arrays with each other. So far I have this

checkScheduleForOverlap: function (dateRanges) {

  var result = dateRanges.reduce((result, current, i, arr) => {
    // console.log(previous, current);

    // get the previous range
    if (i === 0) { return result; }
    var previous = arr[i - 1];

    // Schedule1
    var startTime1 = new Date('1970-01-01T' + previous.StartTime + 'Z');
    var stopTime1 = new Date('1970-01-01T' + previous.StopTime + 'Z');

    // Schedule2
    var startTime2 = new Date('1970-01-01T' + current.StartTime + 'Z');
    var stopTime2 = new Date('1970-01-01T' + current.StopTime + 'Z');

    previous.DaysOfWeek.forEach(function (prevDay) {

      console.log(prevDay);
      current.DaysOfWeek.forEach(function (currDay) {
        console.log(currDay);

        if (prevDay === currDay) {
          var overlap = (startTime1 <= stopTime2) && (stopTime1 >= startTime2);

          // store the result
          if (overlap) {
            // yes, there is overlap
            result.overlap = true;
            // store the specific ranges that overlap
            result.days.push(currDay);
          }
        }
      });
    });

    return result;

    // seed the reduce
  }, { overlap: false, days: [] });

  // return the final results
  console.log(result);
  return result;

}

But it only pares the second array with the first, and the third with the second. It also needs to pare the third with the first. (and if there were 4 schedules, each would need to pare to the other.)

Am I on the right track here, and what can be done to get each DaysOfWeek schedule to pare the StartTime and StopTime with the values in the other schedule?

I created a fake date object using a static day, and I'm only paring the time values.

I'm open to doing it a pletely different way if this is not an efficient way to do it.

Share Improve this question edited Jun 11, 2018 at 23:34 TetraDev asked Jun 11, 2018 at 22:02 TetraDevTetraDev 17.1k7 gold badges64 silver badges63 bronze badges 6
  • 1 infinite number of schedules I'm pretty sure you mean finite. – Keith Commented Jun 11, 2018 at 22:05
  • I mean infinite because a user can add an infinite number of schedules, and all have to be checked against each other for overlaps. – TetraDev Commented Jun 11, 2018 at 22:07
  • 1 a user can add an infinite number of schedules No they can't. – Keith Commented Jun 11, 2018 at 22:08
  • 1 In this universe, it is physically impossible for a user to do something an infinite number of times. You probably want something like Compare each element in an array against every other element. You might be able to use moment-range: github./rotaready/moment-range/blob/master/README.md – CertainPerformance Commented Jun 11, 2018 at 22:38
  • 2 Compare each element in an array against every other element Yes that's correct, I didn't know I was invoking philosophical dilemmas. I updated the title :-D – TetraDev Commented Jun 11, 2018 at 23:34
 |  Show 1 more ment

2 Answers 2

Reset to default 5

Let's focus on the part of your problem that is about paring items between multiple arrays. The actual logic of the parison isn't important for now.

It all starts with a nested for loop:

var arr1 = [ "A", "B", "C" ];
var arr2 = [ "1", "2", "3" ];

// Runs arr1.length * arr2.length = 9 times
for (let i = 0; i < arr1.length; i += 1) {
  for (let j = 0; j < arr2.length; j += 1) {
    console.log(
      "run", i * arr2.length + j, 
      "result", arr1[i], arr2[j]
    );
  }
}

Once you have a function that loops over all pairs of two arrays, what's left to do is to find all possible pairs from a single list of arrays:

const arrays = [ [ "A" ], [ "B" ], [ "C" ] ];

for (let i = 0; i < arrays.length - 1; i += 1) {
//                                ^^^
  for (let j = i + 1; j < arrays.length; j += 1) {
//             ^^^^^
    console.log(
      JSON.stringify(arrays[i]), 
      JSON.stringify(arrays[j])
    );
  }
}

Now that we've got the basics covered, we can chain them together and refactor. I'll have to admit that the refactor is a bit of a personal taste thing, it's perfectly fine to wrap the for loops in functions without the other changes.

I've named the first principle binations, and used reduce and map instead of the for loops. The second for loop is now contained in allPairs.

// Utilities:
const binations = ([xs, ys]) => 
  xs.reduce(
    (cs, x) => cs.concat(ys.map(y => [x, y])),
    []
  );
  
const allPairs = (xs) => 
  xs.reduce(
    (ps, x, i) => ps.concat(xs.slice(i + 1).map(y => [x, y])),
    []
  );

const flatten = xxs => xxs.reduce((xs, ys) => xs.concat(ys))

const findMatches = (matchFn, arrays) => flatten(
    allPairs(arrays).map(binations)).filter(matchFn);

// App:
// Let's just stick to an easy example
const overlap = ([x, y]) => x === y;

console.log(
  findMatches(
    overlap, 
    [ [ 1, 2 ], [ 1, 3 ], [ 1, 2, 3], [ 4, 5 ], [ 1 ] ]
  )
);
  

This approach returns you pairs of overlapping elements. You'll have to include your own overlaps function. You can gain some efficiency by using find instead of filter, which returns the first overlapping pair. If you really want to return as early as possible, before even constructing all pair binations, you'll have to move some more stuff around (but I can't imagine the performance will be an issue).

I was able to get it working using the following code. It could possibly be improved for efficiency but for small lists, it works great.

 /**
 * Compares to parable objects to find out whether they overlap.
 * It is assumed that the interval is in the format [from,to) (read: from is inclusive, to is exclusive).
 * A null value is interpreted as infinity
 */
checkScheduleForOverlap: function (dateRanges) {

  function dateRangeOverlaps (a_start, a_end, b_start, b_end) {
    if (a_start <= b_start && b_start <= a_end) return true; // b starts in a
    if (a_start <= b_end && b_end <= a_end) return true; // b ends in a
    if (b_start < a_start && a_end < b_end) return true; // a in b
    return false;
  }

  function multipleDateRangeOverlaps () {
    var i, j;
    if (arguments.length % 2 !== 0)
      throw new TypeError('Arguments length must be a multiple of 2');
    for (i = 0; i < arguments.length - 2; i += 2) {
      for (j = i + 2; j < arguments.length; j += 2) {
        if (
          dateRangeOverlaps(
            arguments[i], arguments[i + 1],
            arguments[j], arguments[j + 1]
          )
        ) return true;
      }
    }
    return false;
  }

  var result = {
    overlappingDays: [],
    overlap: false
  };

  // for every Schedule
  for (let i = 0; i < dateRanges.length; i++) {
    var current = dateRanges[i];

    // current Schedule
    var startTime1 = current.StartTime;
    var stopTime1 = current.StopTime;

    current.DaysOfWeek.forEach(function (currDay) {

      // console.log('currentScheduleDay', currDay);

      // for every OTHER schedule
      for (let j = 0; j < dateRanges.length; j++) {
        var nextSchedule = dateRanges[j];

        if (j === i) {
          continue;
        }

        nextSchedule.DaysOfWeek.forEach(function (nextDay) {
          // console.log('nextScheduleDay', nextDay);

          if (nextDay === currDay) {

            // next Schedule
            var startTime2 = nextSchedule.StartTime;
            var stopTime2 = nextSchedule.StopTime;

            // var overlap = (startTime1 <= stopTime2) && (stopTime1 >= startTime2);
            var overlap = multipleDateRangeOverlaps(startTime1, stopTime1, startTime2, stopTime2);

            // store the result
            if (overlap) {
              // yes, there is overlap
              result.overlap = true;
              // store the specific ranges that overlap
              result.overlappingDays.push(currDay);
            }

          }
        });
      }

    });
  }

  // remove duplicates in result

  var obj = {};

  for (var i = 0, len = result.overlappingDays.length; i < len; i++) {
    obj[result.overlappingDays[i]] = result.overlappingDays[i];
  }

  result.overlappingDays = new Array();

  for (var key in obj) {
    result.overlappingDays.push(obj[key]);
  }

  if (result.overlappingDays) {
    this.scheduleDaysOverlap = result.overlappingDays;
  }

}
发布评论

评论列表(0)

  1. 暂无评论