Logo
Published on

Array Concatenation

How Array Concatenation Simplifies Data Merging

Mastering array concatenation with the spread operator transforms how you handle multiple data sources. Whether combining API responses, merging paginated results, or joining search data, this technique provides clean, readable syntax that eliminates common pitfalls with traditional concat() methods.

TL;DR

  • Use spread syntax [...arr1, ...arr2] for clean array joining
  • Outperforms concat() with multiple arrays and better readability
  • Essential for combining API responses and paginated data
  • Works seamlessly with dynamic array counts and conditional merging
const merged = [...arr1, ...arr2, ...arr3]

The Array Concatenation Challenge

You're building a dashboard that displays data from multiple API endpoints. The traditional approach uses concat() methods that become unwieldy with multiple data sources, making the code harder to read and prone to mutation bugs.

// The problematic approach with concat()
const searchResults = ['result1', 'result2']
const apiData = ['data1', 'data2', 'data3']
const cachedItems = ['cache1']
function combineDataOldWay(search, api, cache) {
  let combined = search.concat(api)
  combined = combined.concat(cache)
  console.log('Traditional concat result:', combined)
  return combined
}
console.log('Final array:', combineDataOldWay(searchResults, apiData, cachedItems))

Modern array concatenation with spread syntax eliminates mutation concerns and provides crystal-clear intent:

// The elegant spread solution
const searchResults = ['result1', 'result2']
const apiData = ['data1', 'data2', 'data3']
const cachedItems = ['cache1']
function combineDataNewWay(search, api, cache) {
  const combined = [...search, ...api, ...cache]
  console.log('Modern spread result:', combined)
  console.log('Original arrays unchanged:', search.length, api.length, cache.length)
  return combined
}
console.log('Final array:', combineDataNewWay(searchResults, apiData, cachedItems))

Best Practises

Use array concatenation when:

  • ✅ Combining multiple API responses into a single dataset
  • ✅ Merging paginated results from different data sources
  • ✅ Building search results that aggregate from multiple endpoints
  • ✅ Creating composite arrays without mutating original data

Avoid when:

  • 🚩 Working with extremely large arrays (>100k items) where performance matters
  • 🚩 Need to maintain references to original arrays after modification
  • 🚩 Building for environments that don't support ES6 spread syntax
  • 🚩 Simple two-array joins where concat() is more familiar to the team

System Design Trade-offs

AspectSpread SyntaxArray.concat()Array.push()
ReadabilityExcellent - visual clarityGood - familiar methodPoor - imperative style
ImmutabilityImmutable by designCreates new arrayMutates original
Multiple ArraysElegant [...a, ...b, ...c]Chained .concat() callsComplex loops required
PerformanceFast for most use casesSlightly fasterFastest but destructive
Type SafetyWorks with any iterableArray-specific methodArray-specific method
Browser SupportES2015+ requiredUniversal supportUniversal support

More Code Examples

❌ Concat chain nightmare
// Traditional approach with chained concat() calls
function aggregateSearchResults(query) {
  // Simulating multiple API endpoint responses
  const googleResults = [
    { title: 'Google Result 1', source: 'google', score: 0.95 },
    { title: 'Google Result 2', source: 'google', score: 0.87 },
  ]
  const bingResults = [
    { title: 'Bing Result 1', source: 'bing', score: 0.92 },
    { title: 'Bing Result 2', source: 'bing', score: 0.84 },
    { title: 'Bing Result 3', source: 'bing', score: 0.79 },
  ]
  const localResults = [{ title: 'Local Result 1', source: 'local', score: 0.98 }]
  // The painful way with multiple concat operations
  let allResults = []
  allResults = allResults.concat(googleResults)
  allResults = allResults.concat(bingResults)
  allResults = allResults.concat(localResults)
  // Additional processing requires more chaining
  const premiumResults = [{ title: 'Premium Result', source: 'premium', score: 1.0 }]
  allResults = allResults.concat(premiumResults)
  console.log('Traditional concat approach:')
  console.log('Total results:', allResults.length)
  allResults.forEach((result, index) =>
    console.log(`  ${index + 1}. ${result.title} (${result.source})`)
  )
  return allResults.sort((a, b) => b.score - a.score)
}
const traditionalResults = aggregateSearchResults('javascript tips')
console.log('\nTop result:', traditionalResults[0].title)
✅ Spread syntax wins
// Modern approach with elegant spread syntax
function aggregateSearchResultsModern(query) {
  // Simulating multiple API endpoint responses
  const googleResults = [
    { title: 'Google Result 1', source: 'google', score: 0.95 },
    { title: 'Google Result 2', source: 'google', score: 0.87 },
  ]
  const bingResults = [
    { title: 'Bing Result 1', source: 'bing', score: 0.92 },
    { title: 'Bing Result 2', source: 'bing', score: 0.84 },
    { title: 'Bing Result 3', source: 'bing', score: 0.79 },
  ]
  const localResults = [{ title: 'Local Result 1', source: 'local', score: 0.98 }]
  const premiumResults = [{ title: 'Premium Result', source: 'premium', score: 1.0 }]
  // The elegant way with spread syntax
  const allResults = [...googleResults, ...bingResults, ...localResults, ...premiumResults]
  console.log('Modern spread approach:')
  console.log('Total results:', allResults.length)
  allResults.forEach((result, index) =>
    console.log(`  ${index + 1}. ${result.title} (${result.source})`)
  )
  // Easy to add conditional sources
  const hasCache = true
  const cacheResults = hasCache ? [{ title: 'Cached Result', source: 'cache', score: 0.88 }] : []
  const finalResults = [...allResults, ...cacheResults]
  console.log('\nWith cache:', finalResults.length, 'total results')
  return finalResults.sort((a, b) => b.score - a.score)
}
const modernResults = aggregateSearchResultsModern('javascript tips')
console.log('\nTop result:', modernResults[0].title)
console.log('Sources used:', [...new Set(modernResults.map((r) => r.source))])

Technical Trivia

The Reddit Pagination Incident of 2019: Reddit's mobile app experienced a critical bug where user feeds showed duplicate posts after implementing array concatenation for infinite scroll. The issue occurred because developers used push() instead of spread syntax, mutating the original array and causing pagination markers to break.

Why concatenation mattered: The bug manifested when users scrolled quickly - the app would merge new posts with existing ones using push(), which modified the original array. This broke pagination tokens and caused the same posts to appear multiple times, leading to user confusion and engagement drops.

Spread syntax prevents mutations: Using `[...existingPosts, ...newPosts]` creates a new array without touching the originals. This immutable approach ensures pagination state remains consistent, preventing the data corruption that plagued the old implementation and maintaining user experience quality.


Master Array Concatenation: When to Spread

Use spread syntax for array concatenation when combining data from multiple sources, especially API responses and search results. The immutable nature prevents subtle bugs while the readable syntax makes intent crystal clear. Reserve concat() for legacy compatibility or when working with very large arrays where the performance difference matters more than code clarity.