3

I have an array that can contain up to all the days of the week

["mon", "tues", "wed", "thur", "fri", "sat", "sun"]

I am trying to figure out a nice script that would produce the following

Array contains

["mon", "tues", "fri", "sat", "sun"]

Output a string of Mon-Tues & Fri - Sun

I know I can obviously do this with if statements but I can't think of a nice/smart way to do this.

Allreadyhome
  • 1,252
  • 2
  • 25
  • 46

5 Answers5

4
const days = ["mon", "tues", "wed", "thur", "fri", "sat", "sun"];

function group(arr){
  let result = [ [] ], current = result [0];

  for(const day of arr){
    if(days.indexOf(current[current.length-1]) + 1 === days.indexOf(day)) {
      current.push(day);
    } else {
     current = [day];
     result.push(current);
   }
  }

  return result
    .filter(el => el.length)
    .map(el => el.length > 1 ? el[0] +"-"+ el.pop() : el[0])
    .join(" & ");
}

In action

The upper code groups the array into consecutive day groups, then joins them:

group(["mon", "tues","thur", "fri","sun"])
// [[ "mon","tues"], ["thur","fri"], ["sun"]]
// mon - tues & thur - fri & sun
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
2

You could collect all ranges of days in am array and render the result in the structure, you need.

This proposal works for ranges over sunday as well.

var days = ["mon", "tues", "wed", "thur", "fri", "sat", "sun"],
    data = ["mon", "tues", "fri", "sat", "sun"],
    dayNo = {},
    result;

days.forEach((d, i) => dayNo[d] = i);

result = data
    .reduce((r, d, i, dd) => {
        if ((dayNo[dd[i - 1]] + 1) % 7 === dayNo[d]) {
            r[r.length - 1][1] = d;
        } else {
            r.push([d]);
        }
        return r;
    }, [])
    .map(r => r.join(' - '))
    .join(' & ');

console.log(result);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

Spent some time to compare the three proposals to check behavior against invalid inputs and also check total execution time

var reference = ["mon", "tues", "wed", "thur", "fri", "sat", "sun"];

var testcases = [
["mon", "tues", "wed", "thur", "fri", "sat", "sun"],
["mon", "tues", "fri", "sat", "sun"],
["mon", "wed", "fri", "sun"],
["mon", "dupa", "test", "halo", "fri", "sat", "sun"],
["sat", "", "fri", "sat", "sun"],
["wed", "mon", "fri", "tues"],
[""],
[]];

// https://stackoverflow.com/questions/1026069/how-do-i-make-the-first-letter-of-a-string-uppercase-in-javascript
function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

function Marcin(ct, ref) {
    var i0 = 0;
    var i1 = 0;
    var state = 0; // 0 initial 1 later initial 2 inside
    var result = "";
    var len = 0;
    if (ct.length === 0)
        return "";
    while(i0 <= ct.length && i1 <= ref.length) {
    switch(state) {
        case 0:
        case 1:
            while(i1<ref.length && ct[i0] != ref[i1++]);
            if(state == 1)
                result += ' & ';
            result += capitalizeFirstLetter(ct[i0++]);
            state = 2;
            len = 0;
            break;
        case 2:
            if((i0 >= ct.length || i1 >= ref.length) || (ct[i0] != ref[i1])) {
                i0--;
                if (len)
                    result += '-' + capitalizeFirstLetter(ct[i0]);
                state = 1;
            }
            i0++;
            i1++;
            len++;
            break;
    }
    }
    return result;

}

var dayNo = {};
reference.forEach((d, i) => dayNo[d] = i);
//// I modified it here to move building of hash outside of the function

function Nina(data, days) {

  var result;

result = data
    .reduce((r, d, i, dd) => {
        if ((dayNo[dd[i - 1]] + 1) % 7 === dayNo[d]) {
            r[r.length - 1][1] = d;
        } else {
            r.push([d]);
        }
        return r;
    }, [])
    .map(r => r.join(' - '))
    .join(' & ');
   return result;
}

function Jonas(arr, days){
  var result = [ [] ], current = result [0];

  for(var day of arr){
    if( days.indexOf( current[current.length-1] ) +1 === days.indexOf( day) ){
      current.push(day);
    }else{
     current = [day];
     result.push( current);
   }
  }

  return result
    .filter(el => el.length)
    .map(el=> el.length > 1?el[0] +"-"+ el.pop():el[0])
    .join(" & ");
}

var functions=[Marcin, Nina, Jonas];
var vlog = ""
function vlogger(x) {vlog += x + "\n"};

functions.forEach((cv) => {
    vlogger("testing function " + cv.name);
    testcases.forEach((tc) => {
        vlogger("* input " + tc);
        vlogger("* outpt " + cv(tc,reference));
    });    
    var n = 100000;
    var d1 = new Date();
    while(n--) {
        cv(testcases[1], reference);
    }
    var d2 = new Date();
    vlogger("* time " + (d2 - d1));
});

console.log(vlog);

Surprisingly adding hashtable did not help much with overall performance. Even once I have modified the original code to have the hash built only once. Solutions differ also how they behave against out-of-order and out of place inputs.

testing function Marcin
* input mon,tues,wed,thur,fri,sat,sun
* outpt Mon-Sun
* input mon,tues,fri,sat,sun
* outpt Mon-Tues & Fri-Sun
* input mon,wed,fri,sun
* outpt Mon & Wed & Fri & Sun
* input mon,dupa,test,halo,fri,sat,sun
* outpt Mon & Dupa
* input sat,,fri,sat,sun
* outpt Sat & 
* input wed,mon,fri,tues
* outpt Wed & Mon
* input 
* outpt 
* input 
* outpt 
* time 62
testing function Nina
* input mon,tues,wed,thur,fri,sat,sun
* outpt mon - sun
* input mon,tues,fri,sat,sun
* outpt mon - tues & fri - sun
* input mon,wed,fri,sun
* outpt mon & wed & fri & sun
* input mon,dupa,test,halo,fri,sat,sun
* outpt mon & dupa & test & halo & fri - sun
* input sat,,fri,sat,sun
* outpt sat &  & fri - sun
* input wed,mon,fri,tues
* outpt wed & mon & fri & tues
* input 
* outpt 
* input 
* outpt 
* time 170
testing function Jonas
* input mon,tues,wed,thur,fri,sat,sun
* outpt mon-sun
* input mon,tues,fri,sat,sun
* outpt mon-tues & fri-sun
* input mon,wed,fri,sun
* outpt mon & wed & fri & sun
* input mon,dupa,test,halo,fri,sat,sun
* outpt mon & dupa & test & halo & fri-sun
* input sat,,fri,sat,sun
* outpt sat &  & fri-sun
* input wed,mon,fri,tues
* outpt wed & mon & fri & tues
* input 
* outpt 
* input 
* outpt 
* time 142
0

How about creating a array of indexes (+1 to each, so it will be [1, 2, 3..] and we can perform % for continuation, eg: sun > mon again) of your given days with respect to their actual position at the beginning? in that way we can compare each value with last value and if the diff is not 1 then we will start a new group otherwise continue with the existing group.

lets assume you have input ["mon","tues", "fri"] we will map it to [1,2,5] so we can iterate our array and get the diff with previous. let's create an working snippet!

function evaluateContinuation(ip) {
    var days = ["mon", "tues", "wed", "thur", "fri", "sat", "sun"];
    return ip.map(day => 1 + days.indexOf(day)).reduce(function(r, n, i, a) {
        n - a[i-1] % 7 != 1 && r.push([]);
        return r[r.length-1].splice(1, 1, days[n-1]) && r;
    }, []).map(g => g.join('-')).join(' & ');
}

console.log(evaluateContinuation(["mon", "tues", "wed"]))
console.log(evaluateContinuation(["tues", "wed", "fri", "sat", "sun", "mon"]))
console.log(evaluateContinuation(["mon", "wed", "fri", "sat", "sun"]))
Koushik Chatterjee
  • 4,106
  • 3
  • 18
  • 32
-1

Solving this was real fun! Hope this works well for You, enjoy and don't forget to give credits :)

// your code goes here
var reference = ["mon", "tues", "wed", "thur", "fri", "sat", "sun"];

// https://stackoverflow.com/questions/1026069/how-do-i-make-the-first-letter-of-a-string-uppercase-in-javascript
function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

function vmap1(ct, ref) {
    var i0 = 0;
    var i1 = 0;
    var state = 0; // 0 initial 1 later initial 2 inside
    var result = "";
    var len = 0;
    while(i0 <= ct.length && i1 <= ref.length) {
    switch(state) {
        case 0:
        case 1:
            while(i1<ref.length && ct[i0] != ref[i1++]);
            if(state == 1)
                result += ' & ';
            result += capitalizeFirstLetter(ct[i0++]);
            state = 2;
            len = 0;
            break;
        case 2:
            if((i0 >= ct.length || i1 >= ref.length) || (ct[i0] != ref[i1])) {
                i0--;
                if (len)
                    result += '-' + capitalizeFirstLetter(ct[i0]);
                state = 1;
            }
            i0++;
            i1++;
            len++;
            break;
    }
    }
    return result;
}

print(vmap1(["mon", "tues", "wed", "thur", "fri", "sat", "sun"], reference));
print(vmap1(["mon", "tues", "fri", "sat", "sun"], reference));
print(vmap1(["mon", "wed", "fri", "sun"], reference));

Results:

Mon-Sun
Mon-Tues & Fri-Sun
Mon & Wed & Fri & Sun
  • Jonas w, the while loop has a very important role - it increments i1 counter (i1++). Also, happy to have a dialogue here as I think Your answer has too much of computational complexity, as for each day in input You scan thru (indexOf) the reference array twice, not to mention the final mapping. So it's roughly O(n^2) complexity, while O(n) solution as above is sufficient. But what I like about Your answer, is that is so much JavaScript, while I represent the old school state machine based approach :) – Marcin Gałczyński Aug 26 '17 at 14:46
  • well 7 or 7^2 iterations are not really a problem. And that can be optimized easily using a lookup table ( see Ninas answer). And sorry for mentioning the while loop, but its nevertheless ugly... – Jonas Wilms Aug 26 '17 at 14:51
  • No need to be sorry :). For Nina's answer - we're not sure how the lookup table is actually implemented, it might be a hash or a map, and map would give O(n log n), hash would stay at O(n). But then, both the answers iterate multiple times over these arrays (filter/reduce, map, join). They both need temporary memory for the resulting table before joins convert that to text. Can You elaborat what You mean by ugly? Is this the first time You see a state machine concept implemented? – Marcin Gałczyński Aug 26 '17 at 15:01
  • no, good hash implementations have O(1). And show this code to someone else and ask him what it does. I bet he cant answer without trying it. – Jonas Wilms Aug 26 '17 at 15:02
  • Hash itself is O(1) of course and map is O(log n). I was mentioning the complexity for complete solution :) – Marcin Gałczyński Aug 26 '17 at 15:03
  • Not sure why this was down-voted. But motivated me to compare all three solutions for performance and invalid inputs. Surprising result on Nina's version. – Marcin Gałczyński Aug 26 '17 at 18:43