Skip to main content
add type
Source Link
ggorlen
  • 4.2k
  • 2
  • 19
  • 28
/**
 * @typedef {Object} HoursData - Represents an entry containing hour-value data.
 * @property {number} hour - The hour of the entry.
 * @property {number} value - The value associated with the hour.
 */

/**
 * Fills missing hours in a series of hour-value entries.
 * @param {HoursData[]} hours - A series of hour-value entries sorted ascending by hour.
 * @returns {HoursData[]} - A copy of the hours with gaps filled.
 */
const fillMissingHours = hours => {
  const result = [];
  let last;

  for (const entry of hours) {
    for (let i = last + 1; i < entry.hour; i++) {
      result.push({hour: i, value: 0});
    }

    result.push({...entry});
    last = entry.hour;
  }

  return result;
};

const rawData = [
  {hour: 3, value: 3},
  {hour: 5, value: 9},
  {hour: 10, value: 5},
];
console.log(fillMissingHours(rawData));

I used JSDoc instead of TS so this is runnable in the browser, but I assume you have the TS types handled already.

  • Use standard formatting with Prettier (avoid smushed together expressions like i+currentHour+1 and extra spacing like {hour: 10, value: 5} ,).

  • Avoid using the type in the variable namethe type in the variable name, so iString should be named what it is only. i generally means "index" so I'd avoid that in the name too.

  • Your code only works when the input is pre-sorted. That's fine but needs to be documented as an invariant.

  • Don't use in to iterate arrays because it has gotchas you can read about in the docs. Prefer for ... of.

  • Compute the final value once when possible. Consider these lines:

    const gap = nextAvailableHour - currentHour
    const emptyHours = Array(gap-1)
    

    It's great that you've broken out a variable gap, but since it's only used this one time, I'd compute its final value up front:

    const gap = nextAvailableHour - currentHour - 1
    const emptyHours = Array(gap)
    

    This is a subtle distinction, but it's good practice to keep things in one place and be transparent about variables. Otherwise, the code reads like "The gap is nextAvailableHour - currentHour. Just kidding, it's actually - 1 that thing I just wrote on the previous line.".

  • Write a test suite to validate the logic thoroughly. This is the sort of function that can have a lot of edge cases. I was lazy and didn't test this, so if there's a bug, that only proves my point.

This actually isn't too bad, but you can probably see why I went forwith the simpler syntax in my recommendation.

const fillMissingHours = hours => {
  const result = [];
  let last;

  for (const entry of hours) {
    for (let i = last + 1; i < entry.hour; i++) {
      result.push({hour: i, value: 0});
    }

    result.push({...entry});
    last = entry.hour;
  }

  return result;
};

const rawData = [
  {hour: 3, value: 3},
  {hour: 5, value: 9},
  {hour: 10, value: 5},
];
console.log(fillMissingHours(rawData));
  • Use standard formatting with Prettier (avoid smushed together expressions like i+currentHour+1 and extra spacing like {hour: 10, value: 5} ,).

  • Avoid using the type in the variable name, so iString should be named what it is only. i generally means "index" so I'd avoid that in the name too.

  • Don't use in to iterate arrays because it has gotchas you can read about in the docs. Prefer for ... of.

  • Compute the final value once when possible. Consider these lines:

    const gap = nextAvailableHour - currentHour
    const emptyHours = Array(gap-1)
    

    It's great that you've broken out a variable gap, but since it's only used this one time, I'd compute its final value up front:

    const gap = nextAvailableHour - currentHour - 1
    const emptyHours = Array(gap)
    

    This is a subtle distinction, but it's good practice to keep things in one place and be transparent about variables. Otherwise, the code reads like "The gap is nextAvailableHour - currentHour. Just kidding, it's actually - 1 that thing I just wrote on the previous line.".

  • Write a test suite to validate the logic thoroughly. This is the sort of function that can have a lot of edge cases. I was lazy and didn't test this, so if there's a bug, that only proves my point.

This actually isn't too bad, but you can probably see why I went for simpler syntax.

/**
 * @typedef {Object} HoursData - Represents an entry containing hour-value data.
 * @property {number} hour - The hour of the entry.
 * @property {number} value - The value associated with the hour.
 */

/**
 * Fills missing hours in a series of hour-value entries.
 * @param {HoursData[]} hours - A series of hour-value entries sorted ascending by hour.
 * @returns {HoursData[]} - A copy of the hours with gaps filled.
 */
const fillMissingHours = hours => {
  const result = [];
  let last;

  for (const entry of hours) {
    for (let i = last + 1; i < entry.hour; i++) {
      result.push({hour: i, value: 0});
    }

    result.push({...entry});
    last = entry.hour;
  }

  return result;
};

const rawData = [
  {hour: 3, value: 3},
  {hour: 5, value: 9},
  {hour: 10, value: 5},
];
console.log(fillMissingHours(rawData));

I used JSDoc instead of TS so this is runnable in the browser, but I assume you have the TS types handled already.

  • Use standard formatting with Prettier (avoid smushed together expressions like i+currentHour+1 and extra spacing like {hour: 10, value: 5} ,).

  • Avoid using the type in the variable name, so iString should be named what it is only. i generally means "index" so I'd avoid that in the name too.

  • Your code only works when the input is pre-sorted. That's fine but needs to be documented as an invariant.

  • Don't use in to iterate arrays because it has gotchas you can read about in the docs. Prefer for ... of.

  • Compute the final value once when possible. Consider these lines:

    const gap = nextAvailableHour - currentHour
    const emptyHours = Array(gap-1)
    

    It's great that you've broken out a variable gap, but since it's only used this one time, I'd compute its final value up front:

    const gap = nextAvailableHour - currentHour - 1
    const emptyHours = Array(gap)
    

    This is a subtle distinction, but it's good practice to keep things in one place and be transparent about variables. Otherwise, the code reads like "The gap is nextAvailableHour - currentHour. Just kidding, it's actually - 1 that thing I just wrote on the previous line.".

  • Write a test suite to validate the logic thoroughly. This is the sort of function that can have a lot of edge cases. I was lazy and didn't test this, so if there's a bug, that only proves my point.

This actually isn't too bad, but you can probably see why I went with the simpler syntax in my recommendation.

clarify
Source Link
ggorlen
  • 4.2k
  • 2
  • 19
  • 28
  • Use standard formatting with Prettier (avoid smushed together expressions like i+currentHour+1 and strange spacing like {hour: 10, value: 5} ,).

    Use standard formatting with Prettier (avoid smushed together expressions like i+currentHour+1 and extra spacing like {hour: 10, value: 5} ,).

  • Avoid using the type in the variable name, so iString should be named what it is only. i generally means "index" so I'd avoid that in the name too.

    Avoid using the type in the variable name, so iString should be named what it is only. i generally means "index" so I'd avoid that in the name too.

  • Don't use in to iterate arrays because it has gotchas you can read about in the docs. Prefer for ... of.

    Don't use in to iterate arrays because it has gotchas you can read about in the docs. Prefer for ... of.

  • Write a test suite to validate the logic thoroughly. This is the sort of function that can have a lot of edge cases. I was lazy and didn't test this, so if there's a bug, that only proves my point.

    Compute the final value once when possible. Consider these lines:

    const gap = nextAvailableHour - currentHour
    const emptyHours = Array(gap-1)
    

    It's great that you've broken out a variable gap, but since it's only used this one time, I'd compute its final value up front:

    const gap = nextAvailableHour - currentHour - 1
    const emptyHours = Array(gap)
    

    This is a subtle distinction, but it's good practice to keep things in one place and be transparent about variables. Otherwise, the code reads like "The gap is nextAvailableHour - currentHour. Just kidding, it's actually - 1 that thing I just wrote on the previous line.".

  • Write a test suite to validate the logic thoroughly. This is the sort of function that can have a lot of edge cases. I was lazy and didn't test this, so if there's a bug, that only proves my point.

  • Use standard formatting with Prettier (avoid smushed together expressions like i+currentHour+1 and strange spacing like {hour: 10, value: 5} ,).
  • Avoid using the type in the variable name, so iString should be named what it is only. i generally means "index" so I'd avoid that in the name too.
  • Don't use in to iterate arrays because it has gotchas you can read about in the docs. Prefer for ... of.
  • Write a test suite to validate the logic thoroughly. This is the sort of function that can have a lot of edge cases. I was lazy and didn't test this, so if there's a bug, that only proves my point.
  • Use standard formatting with Prettier (avoid smushed together expressions like i+currentHour+1 and extra spacing like {hour: 10, value: 5} ,).

  • Avoid using the type in the variable name, so iString should be named what it is only. i generally means "index" so I'd avoid that in the name too.

  • Don't use in to iterate arrays because it has gotchas you can read about in the docs. Prefer for ... of.

  • Compute the final value once when possible. Consider these lines:

    const gap = nextAvailableHour - currentHour
    const emptyHours = Array(gap-1)
    

    It's great that you've broken out a variable gap, but since it's only used this one time, I'd compute its final value up front:

    const gap = nextAvailableHour - currentHour - 1
    const emptyHours = Array(gap)
    

    This is a subtle distinction, but it's good practice to keep things in one place and be transparent about variables. Otherwise, the code reads like "The gap is nextAvailableHour - currentHour. Just kidding, it's actually - 1 that thing I just wrote on the previous line.".

  • Write a test suite to validate the logic thoroughly. This is the sort of function that can have a lot of edge cases. I was lazy and didn't test this, so if there's a bug, that only proves my point.

add Array version
Source Link
ggorlen
  • 4.2k
  • 2
  • 19
  • 28
const rawData = [
  {hour: 3, value: 3},
  {hour: 5, value: 9},
  {hour: 10, value: 5},
];

const fillMissingHours = hours => {
  const result = [];
  let last;

  for (const entry of hours) {
    for (let i = last + 1; i < entry.hour; i++) {
      result.push({hour: i, value: 0});
    }

    result.push({...entry});
    last = entry.hour;
  }

  return result;
};

const rawData = [
  {hour: 3, value: 3},
  {hour: 5, value: 9},
  {hour: 10, value: 5},
];
console.log(fillMissingHours(rawData));

I'll admit, this is a wee bit clever because I'm relying on the fact that undefined + 1 is NaN which is greater than all other numbers. This means I don't need to use if (last !== undefined) {} around the C-style for loop. Feel free to add that if you prefer.

  • Use standard formatting with Prettier (avoid smushed together expressions like i+currentHour+1 and strange spacing like {hour: 10, value: 5} ,).
  • Avoid using the type in the variable name, so iString should be named what it is only. i generally means "index" so I'd avoid that in the name too.
  • Don't use in to iterate arrays because it has gotchas you can read about in the docs. Prefer for ... of.
  • Write a test suite to validate the logic thoroughly. This is the sort of function that can have a lot of edge cases. I was badlazy and didn't test this, so if there's a bug, that only proves my point.

YourGood points:

  • You're using ===.
  • You have a clear function name.
  • Your code passes ESLint.

Note that your input doesn't quite match your code, which is doing some parsing that doesn't appear necessary since you've shown a pre-parsed array/object data structure. I'm going to assume that's the right input. If you need to parse strings, you can do that as a preliminary step. Use JSON.parse() if the input is JSON rather than rolling your own parser.

Good points:As a final item, here's my solution with Array() for comparison. Note that I'm using .push(...[]) to avoid the explicit inner loop.

  • You're using ===.
  • You have a clear function name.
  • Your code passes ESLint.

const fillMissingHours = hours => {
  const result = [];
  let last;

  for (const entry of hours) {
    if (last !== undefined) {
      const fill = [...Array(entry.hour - last - 1)]
        .map((_, i) => ({hour: last + i + 1, value: 0}));
      result.push(...fill);
    }

    result.push({...entry});
    last = entry.hour;
  }

  return result;
};

const rawData = [
  {hour: 3, value: 3},
  {hour: 5, value: 9},
  {hour: 10, value: 5},
];
console.log(fillMissingHours(rawData));

This actually isn't too bad, but you can probably see why I went for simpler syntax.

const rawData = [
  {hour: 3, value: 3},
  {hour: 5, value: 9},
  {hour: 10, value: 5},
];

const fillMissingHours = hours => {
  const result = [];
  let last;

  for (const entry of hours) {
    for (let i = last + 1; i < entry.hour; i++) {
      result.push({hour: i, value: 0});
    }

    result.push({...entry});
    last = entry.hour;
  }

  return result;
};

console.log(fillMissingHours(rawData));

I'll admit, this is a wee bit clever because I'm relying on the fact that undefined + 1 is NaN which is greater than all other numbers. This means I don't need to use if (last) {} around the C-style for loop. Feel free to add that if you prefer.

  • Use standard formatting with Prettier (avoid smushed together expressions like i+currentHour+1 and strange spacing like {hour: 10, value: 5} ,).
  • Avoid using the type in the variable name, so iString should be named what it is only. i generally means "index" so I'd avoid that in the name too.
  • Don't use in to iterate arrays because it has gotchas you can read about in the docs. Prefer for ... of.
  • Write a test suite to validate the logic thoroughly. This is the sort of function that can have a lot of edge cases. I was bad and didn't test this, so if there's a bug, that only proves my point.

Your input doesn't quite match your code, which is doing some parsing that doesn't appear necessary since you've shown a pre-parsed array/object data structure. I'm going to assume that's the right input. If you need to parse strings, you can do that as a preliminary step. Use JSON.parse() if the input is JSON rather than rolling your own parser.

Good points:

  • You're using ===.
  • You have a clear function name.
  • Your code passes ESLint.
const fillMissingHours = hours => {
  const result = [];
  let last;

  for (const entry of hours) {
    for (let i = last + 1; i < entry.hour; i++) {
      result.push({hour: i, value: 0});
    }

    result.push({...entry});
    last = entry.hour;
  }

  return result;
};

const rawData = [
  {hour: 3, value: 3},
  {hour: 5, value: 9},
  {hour: 10, value: 5},
];
console.log(fillMissingHours(rawData));

I'll admit, this is a wee bit clever because I'm relying on the fact that undefined + 1 is NaN which is greater than all other numbers. This means I don't need to use if (last !== undefined) {} around the C-style for loop. Feel free to add that if you prefer.

  • Use standard formatting with Prettier (avoid smushed together expressions like i+currentHour+1 and strange spacing like {hour: 10, value: 5} ,).
  • Avoid using the type in the variable name, so iString should be named what it is only. i generally means "index" so I'd avoid that in the name too.
  • Don't use in to iterate arrays because it has gotchas you can read about in the docs. Prefer for ... of.
  • Write a test suite to validate the logic thoroughly. This is the sort of function that can have a lot of edge cases. I was lazy and didn't test this, so if there's a bug, that only proves my point.

Good points:

  • You're using ===.
  • You have a clear function name.
  • Your code passes ESLint.

Note that your input doesn't quite match your code, which is doing some parsing that doesn't appear necessary since you've shown a pre-parsed array/object data structure. I'm going to assume that's the right input. If you need to parse strings, you can do that as a preliminary step. Use JSON.parse() if the input is JSON rather than rolling your own parser.

As a final item, here's my solution with Array() for comparison. Note that I'm using .push(...[]) to avoid the explicit inner loop.

const fillMissingHours = hours => {
  const result = [];
  let last;

  for (const entry of hours) {
    if (last !== undefined) {
      const fill = [...Array(entry.hour - last - 1)]
        .map((_, i) => ({hour: last + i + 1, value: 0}));
      result.push(...fill);
    }

    result.push({...entry});
    last = entry.hour;
  }

  return result;
};

const rawData = [
  {hour: 3, value: 3},
  {hour: 5, value: 9},
  {hour: 10, value: 5},
];
console.log(fillMissingHours(rawData));

This actually isn't too bad, but you can probably see why I went for simpler syntax.

clarify
Source Link
ggorlen
  • 4.2k
  • 2
  • 19
  • 28
Loading
Source Link
ggorlen
  • 4.2k
  • 2
  • 19
  • 28
Loading