如何根据常见的子字符串模式减少和 mutate/change 数组的字符串条目?

How does one reduce and mutate/change string entries of an array based on common substring patterns?

我有一个字符串项数组...

[
  'Mon  : 9:00AM - 7:00PM',
  'Tue  : 9:00AM - 10:00PM',
  'Wed  : Closed',
  'Thu  : 9:00AM - 7:00PM',
  'Fri  : 9:00AM - 7:00PM',
  'Sat  :  Closed',
  'Sun  :  Closed',
]

...我想达到如下所示的结果...

[
  'Mon: 9:00AM - 7:00PM',
  'Tue: 9:00AM - 10:00PM',
  'Wed: Closed',
  'Thu-Fri: 9:00AM - 7:00PM',
  'Sat-Sun:  Closed',
]

非常感谢任何帮助。

  1. 首先需要将单个开放时间字符串的 day 值与 hours 值部分分开。

    • 这可以通过 indexOf, substring and trim ...

      实现
      function splitOpeningHoursEntry(entry) {
        // e.g.: 'Mon  : 9:00AM - 7:00PM'
        const indexOfColon = entry.indexOf(':'); // e.g. 5
      
        // entry.substring(0, 5) ... e.g.: 'Mon  '
        const day = entry.substring(0, indexOfColon);
        // entry.substring(6) ... e.g.: ' 9:00AM - 7:00PM'
        const hours = entry.substring(indexOfColon + 1);
      
        // e.g.: ['Mon', '9:00AM - 7:00PM']
        return [day.trim(), hours.trim()];
      }
      
    • ... 或者可以通过 split-ting with a Regular Expression like ... /(^[^:\s]+)\s*:\s*/ ... and slice-ing 结果数组来完成...

      function regexSplitOpeningHoursEntry(entry) {
        // [https://regex101.com/r/vGRck7/3]
        // entry.split(':')                 // ["Mon  ", " 9", "00AM - 7", "00PM"]
      
        // [https://regex101.com/r/vGRck7/2]
        // entry.split(/\s*:\s*/)           // ["Mon", "9", "00AM - 7", "00PM"]
      
        // [https://regex101.com/r/vGRck7/1]
        // entry.split(/(^[^:\s]+)\s*:\s*/) // ["", "Mon", "9:00AM - 7:00PM"];
      
        return entry.split(/(^[^:\s]+)\s*:\s*/).slice(1);
      }
      
  2. 然后必须将整个营业时间字符串数组 map 放入一个数组数组中,其中每个数组项包含第一个 day 值和 hours 值作为第二个数组项...或者像这样...

    sampleList.map(splitOpeningHoursEntry);
    

    ...或类似...

    sampleList.map(regexSplitOpeningHoursEntry);
    
  3. 最重要的是需要 reduce 将这个拆分的 [<day>, <hours>] 条目数组转换为紧凑形式 ...

  4. 最后必须 map 每个拆分的 [<day>, <hours>] 条目 concatenation task 回到其人类可读的字符串形式....

const sampleList = [
  'Mon  : 9:00AM - 7:00PM',
  'Tue  : 9:00AM - 10:00PM',
  'Wed  : Closed',
  'Thu  : 9:00AM - 7:00PM',
  'Fri  : 9:00AM - 7:00PM',
  'Sat  :  Closed',
  'Sun  :  Closed',
];

function splitOpeningHoursEntry(entry) {
  // e.g.: 'Mon  : 9:00AM - 7:00PM'
  const indexOfColon = entry.indexOf(':'); // e.g. 5

  // entry.substring(0, 5) ... e.g.: 'Mon  '
  const day = entry.substring(0, indexOfColon);
  // entry.substring(6) ... e.g.: ' 9:00AM - 7:00PM'
  const hours = entry.substring(indexOfColon + 1);

  // e.g.: ['Mon', '9:00AM - 7:00PM']
  return [day.trim(), hours.trim()];
}
function regexSplitOpeningHoursEntry(entry) {
  // [https://regex101.com/r/vGRck7/3]
  // entry.split(':')                 // ["Mon  ", " 9", "00AM - 7", "00PM"]

  // [https://regex101.com/r/vGRck7/2]
  // entry.split(/\s*:\s*/)           // ["Mon", "9", "00AM - 7", "00PM"]

  // [https://regex101.com/r/vGRck7/1]
  // entry.split(/(^[^:\s]+)\s*:\s*/) // ["", "Mon", "9:00AM - 7:00PM"];

  return entry.split(/(^[^:\s]+)\s*:\s*/).slice(1);
}

function compactOpeningHoursEntries(compactEntries, splitEntry, idx, arr) {
  // get the predecessor item of the currently
  // processed `splitEntry` item or default to [].
  const prevSplitEntry = arr[idx - 1] || [];

  // get the successor item of the currently
  // processed `splitEntry` item or default to [].
  const nextSplitEntry = arr[idx + 1] || [];

  if (prevSplitEntry[1] !== splitEntry[1]) {
    // in case the previous and current `hours` values do not match ...

    // ... push the current entry of splitted `day` and `hours`
    // values into `compactEntries` which is the accumulating
    // array of the compacted form of all opening hours entries.
    compactEntries.push(splitEntry);

  } else if (nextSplitEntry[1] !== splitEntry[1]) {
    // ... or in case the next and current `hours` values do not match ...

    const lastCompactEntry = compactEntries[compactEntries.length - 1];

    // ...retrieve the first and the last day value
    // of a compactly written day-range format...
    const firstDayInRange = lastCompactEntry[0];
    const lastDayInRange = splitEntry[0];

    // ...and create and rewrite its compact form
    // as the compacted entry's final day value.
    lastCompactEntry[0] = firstDayInRange + '-' + lastDayInRange;
  }
  return compactEntries;
}

function concatOpeningHoursEntry([day, hours]) {
  return `${ day }: ${ hours }`;
}

// First one needs to separate the `day` from the
// `hours` part of a single opening hours string
console.log(
  "splitOpeningHoursEntry('Mon  : 9:00AM - 7:00PM') ...",
  splitOpeningHoursEntry('Mon  : 9:00AM - 7:00PM')
);
console.log(
  "regexSplitOpeningHoursEntry('Mon  : 9:00AM - 7:00PM') ...",
  regexSplitOpeningHoursEntry('Mon  : 9:00AM - 7:00PM')
);

// Then one does map an entire array of opening hours strings
// into an array of arrays, where each array item contains the
// `day` value as first and the `hours` value as second array item.
console.log(
  '... list item `split` mapping ... ',
  sampleList
    .map(splitOpeningHoursEntry)
  //.map(regexSplitOpeningHoursEntry)
)

// On top one has to `reduce` this array of splitted
// `[<day>, <hours>]` entries into its compact form.
console.log(
  '... list item `split` mapping and split entry reducing ... ',
  sampleList
    .map(splitOpeningHoursEntry)
    .reduce(compactOpeningHoursEntries, [])
);

// Finally one needs to `map` each splitted `[<day>, <hours>]` entry
// with a concatenation task back into its human readable string form.
console.log(
  '... list item `split` mapping, reducing and a final concatenation mapping ... ',
  sampleList
    .map(splitOpeningHoursEntry)
    .reduce(compactOpeningHoursEntries, [])
    .map(concatOpeningHoursEntry)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

另一个不那么多话的概念证明......

function splitOpeningHoursEntry(entry) {
  return entry.split(/(^[^:\s]+)\s*:\s*/).slice(1);
}
function concatOpeningHoursEntry([day, hours]) {
  return `${ day }: ${ hours }`;
}

function compactOpeningHoursEntries(compactEntries, splitEntry, idx, arr) {
  const prevSplitEntry = arr[idx - 1] || [];
  const nextSplitEntry = arr[idx + 1] || [];

  if (prevSplitEntry[1] !== splitEntry[1]) {

    compactEntries.push(splitEntry);

  } else if (nextSplitEntry[1] !== splitEntry[1]) {
    const lastCompactEntry = compactEntries[compactEntries.length - 1];

    const firstDayInRange = lastCompactEntry[0];
    const lastDayInRange = splitEntry[0];

    lastCompactEntry[0] = firstDayInRange + '-' + lastDayInRange;
  }
  return compactEntries;
}
console.log([
    'Mon  : 08:00AM - 17:00PM',
    'Tue  : 08:00AM - 17:00PM',
    'Wed  : 08:00AM - 17:00PM',
    'Thu  : 10:00AM - 14:00PM',
    'Fri  : 10:00AM - 14:00PM',
    'Sat  :  Closed',
    'Sun  :  Closed',
  ], '=>', [
    'Mon  : 08:00AM - 17:00PM',
    'Tue  : 08:00AM - 17:00PM',
    'Wed  : 08:00AM - 17:00PM',
    'Thu  : 10:00AM - 14:00PM',
    'Fri  : 10:00AM - 14:00PM',
    'Sat  :  Closed',
    'Sun  :  Closed',
  ]
  .map(splitOpeningHoursEntry)
  .reduce(compactOpeningHoursEntries, [])
  .map(concatOpeningHoursEntry)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }