Home > Software engineering >  Javascript: array difference but occurrencies between parentheses
Javascript: array difference but occurrencies between parentheses

Time:10-04

Say we have 2 arrays

a = ['1', '2', '3', '4', '5']
b = ['1', '3', '5']

The difference between these 2 arrays

diff = a.filter(x => !b.includes(x));

so diff is equal to ['2', '4']

My problem is that I could have

a = ['1', '2', '3(4)', '4', '5'] // (I have 4 occurrencies of "3")
b = ['1', '3', '5'] 
// or 
b = ['1', '3(3)', '5']

and I want to get this array

diff = ['2', '3(3)', '4'] 
// or 
diff = ['2', '3', '4']

CodePudding user response:

Your data format is awkward. If you can take decisions on that, I advise you to do instead (for an array like ['1', '2', '3(4)', '4', '5']):

  • use a flat array with as many occurrences as it gets for each element, like:

     ['1', '2', '3', '3', '3', '3', '4', '5']
    
  • use an array of tuples, whose second value in each element is its count, like:

     [['1',1], ['2',1], ['3',4], ['4',1], ['5',1]]
    
  • use a map-like object, whose value is the count, like:

     {'1':1, '2':1, '3':4, '4':1, '5':1}
    
  • use an array of only the counts, where the index is implicitly the key:

     [0, 1, 1, 4, 1, 1]
    

Any of those would make your like easier.


If none of that is a valid option for you, I will point you in the right direction of implementing a function to compare 2 elements are yield the merged difference value according to the rules you have:

function diff(s1,s2) {
  const re = /([0-9] )(?:\(([0-9] )\))?/
  const [,n1,q1] = re.exec(s1)
  const [,n2,q2] = re.exec(s2)
  if (n1!=n2) return  
  const q = Number(q1||1) - Number(q2||1)
  if (q <= 0) return 
  if (q == 1) return n1
  return n1   `(${q})`
}

console.log(diff("3(4)", "3"   )) // "3(3)"
console.log(diff("3(4)", "3(2)")) // "3(2)"
console.log(diff("3(4)", "3(3)")) // "3"
console.log(diff("3(4)", "3(4)")) // undefined
console.log(diff("3(4)", "3(5)")) // undefined
console.log(diff("3"   , "3"   )) // undefined
console.log(diff("3"   , "3(2)")) // undefined
console.log(diff("3"   , "5"   )) // undefined

You can start from here to iterate through your arrays and generate the differences with the merged values.

CodePudding user response:

You have a task of subtracting 2 occurrence maps: each record represents how many times value occurred somewhere.

So, split this task into steps.

  1. Convert array to a more direct representation (a Map)
  2. Implement a subtract logic
  3. Convert occurrence map back to the array representation.

const a = ['1', '2', '3(4)', '4', '5']
const b = ['1', '3(2)', '5']


function subtractMaps(a, b) {
  const result = new Map(a.entries())
  for (const [k, bValue] of b.entries()) {
    const aValue = a.get(k)

    if (!aValue) continue;

    const diff = aValue - bValue;

    if (diff > 0) {
      result.set(k, diff);
    } else {
      result.delete(k)
    }
  }

  return result;
}

function mapFromArray(arr) {
  return new Map(arr.map(k => {
    const [key, val = 1] = k.split(/\(|\)/)
    return [key, Number(val)]
  }))
}

function mapToArray(map) {
  return [...map.entries()].map(([k, v]) => v > 1 ? `${k}(${v})` : k)
}

console.log(
  mapToArray(
   subtractMaps(mapFromArray(a), mapFromArray(b))
  )
)

CodePudding user response:

I think the trick is to re-define each element as an object you can work with:

12(3) will be equivalent to:

{
  value: 12
  times: 3
}

or 8

{
  value: 8
  times: 1
}

Then you can create a dictionary and do calculations.

Try this:

const a = ['1', '2', '3(4)', '4', '5'] // (I have 4 occurrencies of "3")
const b = ['1', '3', '5'] 

console.log(diff(a,b))

function diff(arr, minusArr)
{
  const dict = arr.reduce(
    (dict, current) => {
      const {value, times} = unpack(current)
      dict[value] = (dict[value]?.times ?? 0)   times
      return dict
    }
    ,{}
  )

  minusArr.forEach(
    (el)=> {
      const {value, times} = unpack(el)
      if(dict[value] == false) return

      dict[value] = 
        dict[value] <= times
          ? 0
          : dict[value] - times
    }
  )

  return Object.entries(dict)
    .map(([value, times])=> pack({value, times}))
    .filter(el => el !== '')
}

function unpack(str)
{
  return {
    value: parseInt(str).toString(),
    times: parseInt(str.match(/\(([0-9] )\)/i)?.[1] ?? 1)
  }
}

function pack({value, times})
{
  if(times === 1) return value
  if(times === 0) return ''
  return `${value}(${times})`
}

  • Related