Matt Larsuma
Matt Larsuma

Reputation: 1519

JavaScript loop through array, remove duplicates per one value, then push to new array with that value and one other

I have an array of objects coming to my Vuejs front-end via api call and I am attempting to loop through, remove duplicates, then return a new array with unique "phases" and their associated "id's". The original array has several other key/values I do not need. I am also sorting them in order by the phase number. Here's my code:

salesPhases () {
  let phases = this.$store.state.addresses.salesPhases
  let uniquePhases = []
  for (let i = 0; i < phases.length; i++) {
    if (uniquePhases.indexOf(phases[i].phase_number) === -1) {
      uniquePhases.push(phases[i].phase_number)
    }
  }
  return uniquePhases.sort((a, b) => {
    return a - b
  })
}

The above code works for everything I need, minus including the id. Here's my attempt at doing that, which then negates the unique phases condition.

uniquePhases.push([phases[i].phase_number, phases[i].id])

The sort still works, but it is then sorting one big single-dimensional array. The array of phases looks something like this:

{ "data": [
    {
        "id": "94e224af-135f-af31-3619-535acfae9930",
        "street_address1": "407 48TH ST E",
        "street_address2": null,
        "phase": "101",
        "sales_rep": "164",
        "id": "abd90d6b-28a8-2be6-d6c1-abd9007aef38",
        "name": "48TH ST E",
        "block_minimum": 400,
        "block_maximum": 498,
        "side": 2
    },
    {
        "id": "94e224af-135f-af31-3619-535acfae9930",
        "street_address1": "407 48TH ST E",
        "street_address2": null,
        "phase": "101",
        "sales_rep": "164",
        "id": "abd90d6b-28a8-2be6-d6c1-abd9007aef38",
        "name": "48TH ST E",
        "block_minimum": 401,
        "block_maximum": 499,
        "side": 1
    }
]

Upvotes: 1

Views: 1483

Answers (3)

Ori Drori
Ori Drori

Reputation: 192016

You can filter the array to get only unique, using a Set, then map the items into new objects that contain only the id and the phase_number, and then sort by the phase_number:

salesPhases () {
  const uSet = new Set()
  return this.$store.state.addresses.salesPhases
    .filter(({ phase_number }) => uSet.has(phase_number) ? false : uSet.add(phase_number)) // get uniques by phase_number
    .map(({ id, phase_number }) => ({ id, phase_number })) // get an object with the id and the phase_number
    .sort((a, b) => a.phase_number - b.phase_number) // sort by phase_number
}

You can also use reduce and Map, and then spread the Map.values() iterator to an array

salesPhases () {
  return [...this.$store.state.addresses.salesPhases
    .reduce((m, { id, phase_number }) => 
      m.has(phase_number) ? m : m.set(phase_number, { id, phase_number }), new Map()) // set to map, if phase_number key doesn't exist
    .values()] // convert the Map values to an array
    .sort((a, b) => a.phase_number - b.phase_number) // sort by phase_number
}

Upvotes: 4

James Gilchrist
James Gilchrist

Reputation: 2312

I would leverage lodash for this: https://lodash.com/docs#sortedUniqBy

salesPhases () {
  return _.sortedUniqBy(this.$store.state.addresses.salesPhases, 
    phase => phase.phase_number)
}

Upvotes: 0

Alex Yuly
Alex Yuly

Reputation: 180

One solution is to update your code to push the entire phase object, rather than just the phase number, and then use the Array find method instead of indexOf, to check if a phase with the given number is found.

Try this:

salesPhases () {
  let phases = this.$store.state.addresses.salesPhases
  let uniquePhases = []
  for (let i = 0; i < phases.length; i++) {
    if (!uniquePhases.find(x => x.phase_number === phases[i].phase_number)) {
      uniquePhases.push(phases[i])
    }
  }
  return uniquePhases.sort((a, b) => {
    return a.phase_number - b.phase_number
  })
}

Upvotes: 0

Related Questions