Search code examples
javascriptalgorithmcolumnsorting

Javascript - Finding distinct properties in array of objects (matrix)


Background: Pre-processing code will run on a lot of data to extract the properties that needs to be shown on a chart. The chart is a parallel coordinates chart. It will run in browser.

Snippet: Extracts the properties that changed and the related category

My snippet outputs the answer correctly, but not efficiently

var all = [
    {
      Dim_A: { v: '0', category: 'Window' },
      Dim_B: { v: '-1', category: 'Wall' },
      Dim_C: { v: '0', category: 'Wall' },
      Dim_D: { v: '0', category: 'Exterior Wall' }
    },
    {
      Dim_A: { v: '0', category: 'Window' },
      Dim_B: { v: '2', category: 'Wall' },
      Dim_C: { v: '1', category: 'Wall' },
      Dim_D: { v: '0', category: 'Exterior Wall' }
    },
    {
      Dim_A: { v: '0', category: 'Window' },
      Dim_B: { v: '-1', category: 'Wall' },
      Dim_C: { v: '0', category: 'Wall' }
    },
    {
      Dim_A: { v: '0', category: 'Window' },
      Dim_B: { v: '-1', category: 'Wall' },
      Dim_C: { v: '0', category: 'Wall' },
      Dim_E: { v: '0', category: 'Exterior Wall' }
    }
  ]

  if (all.length < 2) {
    throw 'need at least 2'
  }

  let distinctDims = new Map()
  for (let i = 0; i < all.length - 1; i++) {
    for (let j = i + 1; j < all.length; j++) {
      if (i === j) {
        continue
      }
      let a = all[i]
      let b = all[j]
      for (let pa in a) {
        if (!a[pa] || !b[pa] || a[pa].v !== b[pa].v)
          distinctDims[pa] = { category: a[pa].category }
      }
      for (let pb in b) {
        if (!a[pb] || !b[pb] || a[pb].v !== b[pb].v)
          distinctDims[pb] = { category: b[pb].category }
      }
    }
  }

  console.log(distinctDims)

This outputs correctly. Looking for a more efficient way.


Solution

  • I am making an assumption that by changed we include properties that exist at one point and not at another. That seems to be the basis for including properties Dim_D and Dim_E in the changes. If my assumption is correct, a single loop is sufficient to find the changes. There are three possible cases for a change:

    • a value changes from the original at all[0]
    • a value exists in all[0] but ceases to exist later
    • a value does not exist in all[0] but appears later
    if (all.length < 2) {
      throw 'need at least 2'
    }
    
    const prevDims = all[0]
    const distinctDims = new Map()
    
    for (let i = 1; i < all.length; i++) {
      const currentDims = all[i];
    
      for (const key in currentDims) {
        if (!distinctDims[key]) {
          // add to distinctDims either if value changed, or if it was not present previously 
          if (!prevDims[key] || currentDims[key].v !== prevDims[key].v) {
              distinctDims[key] = { 
                category: currentDims[key].category 
              }
          }
        }
      }
    
    
      // Check for dimensions that existed previously but not currently
      for (const key in prevDims) {
        if (!distinctDims[key] && !currentDims[key]) {
          distinctDims[key] = { category: prevDims[key].category }
          delete prevDims[key]
        }
      }
    }