index.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. /**
  2. * Like `Array#splice`, but smarter for giant arrays.
  3. *
  4. * `Array#splice` takes all items to be inserted as individual argument which
  5. * causes a stack overflow in V8 when trying to insert 100k items for instance.
  6. *
  7. * Otherwise, this does not return the removed items, and takes `items` as an
  8. * array instead of rest parameters.
  9. *
  10. * @template {unknown} T
  11. * Item type.
  12. * @param {Array<T>} list
  13. * List to operate on.
  14. * @param {number} start
  15. * Index to remove/insert at (can be negative).
  16. * @param {number} remove
  17. * Number of items to remove.
  18. * @param {Array<T>} items
  19. * Items to inject into `list`.
  20. * @returns {undefined}
  21. * Nothing.
  22. */
  23. export function splice(list, start, remove, items) {
  24. const end = list.length
  25. let chunkStart = 0
  26. /** @type {Array<unknown>} */
  27. let parameters
  28. // Make start between zero and `end` (included).
  29. if (start < 0) {
  30. start = -start > end ? 0 : end + start
  31. } else {
  32. start = start > end ? end : start
  33. }
  34. remove = remove > 0 ? remove : 0
  35. // No need to chunk the items if there’s only a couple (10k) items.
  36. if (items.length < 10000) {
  37. parameters = Array.from(items)
  38. parameters.unshift(start, remove)
  39. // @ts-expect-error Hush, it’s fine.
  40. list.splice(...parameters)
  41. } else {
  42. // Delete `remove` items starting from `start`
  43. if (remove) list.splice(start, remove)
  44. // Insert the items in chunks to not cause stack overflows.
  45. while (chunkStart < items.length) {
  46. parameters = items.slice(chunkStart, chunkStart + 10000)
  47. parameters.unshift(start, 0)
  48. // @ts-expect-error Hush, it’s fine.
  49. list.splice(...parameters)
  50. chunkStart += 10000
  51. start += 10000
  52. }
  53. }
  54. }
  55. /**
  56. * Append `items` (an array) at the end of `list` (another array).
  57. * When `list` was empty, returns `items` instead.
  58. *
  59. * This prevents a potentially expensive operation when `list` is empty,
  60. * and adds items in batches to prevent V8 from hanging.
  61. *
  62. * @template {unknown} T
  63. * Item type.
  64. * @param {Array<T>} list
  65. * List to operate on.
  66. * @param {Array<T>} items
  67. * Items to add to `list`.
  68. * @returns {Array<T>}
  69. * Either `list` or `items`.
  70. */
  71. export function push(list, items) {
  72. if (list.length > 0) {
  73. splice(list, list.length, 0, items)
  74. return list
  75. }
  76. return items
  77. }