Paul
Paul

Reputation: 4438

Js return the start date and end date on a string

I have strings like this:

(Feb 28-Mar 1)
(Mar 2-3)

I would like me to return an object as you can see below, someone to give some suggestion how can I do?

    function rD(text){
    let date = text.replace('(', '').replace(')', '').split(' ');
    //const [start, end] = date[2].split("-").map(Number);
    return date;
    }
    
    
    console.log(rD("(Feb 28-Mar 1)"))
    console.log(rD("(Mar 2-3)"))

Return:

[
{
  month: 2,
  day: 28
},
{
  month: 3,
  day: 1
}
]

[
{
  month: 3,
  day: 2
},
{
  month: 3,
  day: 3
}
]

Upvotes: 0

Views: 473

Answers (6)

Rohìt Jíndal
Rohìt Jíndal

Reputation: 27202

Added all the steps inline in below code snippet.

// const str = "(Feb 28-Mar 1)";
const str = "(Mar 2-3)";

// An Object which contains numeric value of months.
const monthMap = {
  Jan: 1,
  Feb: 2,
  Mar: 3
};

/**
 * convertStringToObject() method used to convert the input string into an object.
 * @param : inputString
 */
function convertStringToObject(inputString) {
  // Removing open and closed paranthesis and splitting the inputString with '-'
    const split = inputString.replace('(', '').replace(')', '').split('-');
  // If splitted array element does not contain month, appending that. 
  split[1] = (split[1].trim().length === 1) ? split[0].split(' ')[0] + ' ' + split[1] : split[1];
  // Looping over the splitted array and then creating the object with keys 'month' and 'day'
  return split.map((item) => {
    const splittedItem = item.split(' ');
    return {
        month: monthMap[splittedItem[0]],
      day: Number(splittedItem[1])
    }
  });
}

// Calling the convertStringToObject() method by passing the input string as a param.
console.log(convertStringToObject(str));

Upvotes: 0

code
code

Reputation: 6319

Besides the code, the advantage to this approach is that it works if the input is something like Jan 1-3-Mar 7, while all the other answers don't put this into consideration, thus returning undefined or an error.

Simple regex and a list of month to numbers should work, and it seems the most straightforward to me. Using String.prototype.match we can "ignore" all the extra data (i.e. the parentheses and dashes between months), just extract the necessary data:

function rD(str) {
  // have a list of the numbers of each month
  const monthNum = {
    jan: 1,
    feb: 2,
    mar: 3,
    apr: 4,
    may: 5,
    jun: 6,
    jul: 7,
    aug: 8,
    sep: 9,
    oct: 10,
    nov: 11,
    dec: 12
  };

  // regex to extract the patterns "Feb 23" or "Aug 15-23"
  let spl = str.match(/[a-z]{3} \d{1,2}(\-\d{1,2})?/gi);

  // loop through matches
  const result = spl.map(s => {
    // first 3 letters is the month; get the month number
    const month = monthNum[s.substr(0, 3).toLowerCase()],
      rest = s.substr(4); // get the rest of the string sans the month and extra space
    const e = rest.split("-");
    return e.map(q => ({
      month,
      day: +q
    }));
  }).flat(); // our array may occasionally be multidimensional if the user provides something like "Nov 7-12". We flatten the array to fix that
  return result;
}

console.log("(Feb 28-Mar 1):", rD("(Feb 28-Mar 1)"));
console.log("(Mar 2-3):", rD("(Mar 2-3)"));
console.log("(Nov 7-12-Dec 15):", rD("(Nov 7-12-Dec 15)"));
.as-console-wrapper { min-height: 100% !important; }

Upvotes: 0

derpirscher
derpirscher

Reputation: 17390

I'd remove the parentheses first and then split by /[ -]/. This way you get an array in one of these two forms

["Feb", "28", "Mar", "1"]

or

["Mar", "2", "3"]

Now if the array has 4 elements the first and third are always the month and second and forth are the day. If the array has 3 elements, the first is a month for both, start and end, the second and third are the days.

For getting the number of the month you can have a simple lookup like

{ Jan:1, Feb:2, ... }

let months = { Jan: 1, Feb: 2, Mar: 3 /*  you get the idea*/}
let spans = ["(Jan 28 - Feb 3)", "(Mar 1-3)"]

let parse = (span) => {
  let parts = span.replace(/[()]/g, "").split(/[ -]/).filter(x => !!x);
  switch (parts.length) {
    case 4: return [{month: months[parts[0]], date: +parts[1]}, {month: months[parts[2]], date: +parts[3]}];
    case 3: return [{month: months[parts[0]], date: +parts[1]}, {month: months[parts[0]], date: +parts[2]}];
    default: return undefined;
  }
}


console.log(parse(spans[0]));
console.log(parse(spans[1]))

Upvotes: 2

フィン
フィン

Reputation: 21

First we are going to create a mapper for the months. like this:

let MonthsMapper = new Map([['Jan', 1], ['Feb', 2], ['Mar', 3] /*...*/])

Then we need a function which cutes the string into chunks by removing the parenthesis and splitting it by its hyphen. The first chunks are the start and end dates. With these two dates we can further get the start month, start day, end month, and end day. (By splitting our chunks by there whitespaces)

There is only one special case I can see from your example and that is the case when the end date does not specify a month, in which case it is implicitly the start month.

let DateObjectParser = (dateString) => {
    const [startDate, endDate] = dateString.replace(/[()]/g, '').split('-')
    const [startMonth, startDay] = startDate.split(' ')
    let [endMonth, endDay] = endDate.split(' ')

    // in case a second month is not provided
    if (endDay === undefined) {
        endDay = endMonth
        endMonth = startMonth
    }

    return [
       {
           month: MonthsMapper.get(startMonth),
           day: parseInt(startDay),
       },
       {
            month: MonthsMapper.get(endMonth),
            day: parseInt(endDay),
       }
  ]
}

Upvotes: 1

Terry Lennox
Terry Lennox

Reputation: 30685

I'd suggest using a regex pattern to parse each span.

From this we can get the startMonth, startDay, endMonth, endDay. We can then create a getMonthNumber() function to turn the abbreviated month name (Jan, Feb, etc.) to a number.

function getMonthNumber(month) {
    const lookup = { jan: 01, feb: 02, mar: 03, apr: 04, may: 05, jun: 06, jul: 07, aug: 08, sep: 09, oct: 10, nov: 11, dec: 12};
    return lookup[(month + '').toLowerCase()]
}

function parseSpan(str) {
    const pattern = /\(([a-z]{3})\s+(\d{1,2})\-([a-z]{3})?\s?(\d{1,2})\)/i
    const [, startMonth, startDay, endMonth, endDay] = str.match(pattern);
    return [
        { month: getMonthNumber(startMonth), day: +startDay },
        { month: getMonthNumber(endMonth || startMonth), day: +endDay }
    ];
}

let testInputs = [
  '(Feb 28-Mar 1)',
  '(Mar 2-3)',
  '(Sep 28-Oct 31)',
  '(Jan 3-17)'
]

testInputs.map(parseSpan).forEach(span => console.log(span))
.as-console-wrapper { max-height: 100% !important; }

Upvotes: 2

Sundaramoorthy Anandh
Sundaramoorthy Anandh

Reputation: 601

You can try this

function rangeCalcFunc(range = null) {
  if(range && range.length){
    const [start, end] = range.substring(1, range.length-1).split("-");
    console.log(start);console.log(end);
    const [startMon, startDt] = start.split(" ");
    const [endMon, endDt] = end.split(" ");
    
    return [
    {
      month: calcMonthInNumber(startMon.trim()),
      date: startDt
    },
    {
      month: calcMonthInNumber(endMon.trim()),
      date: endDt
    }
    ]
  }
}

function calcMonthInNumber(month) {
    switch(month.toLowerCase()){
      case 'jan': return '01'
      case 'feb': return '02'
      //add for other months
      default: break;
    }
}

console.log(rangeCalcFunc("(Jan 28-Feb 1)"));

Upvotes: 1

Related Questions