1

I am developing an E-Commerce using Javascript. In the section of the products of the page, I want to implement the functionality to change the order of the products setting the number in the corresponding input. You can see an image to get an idea of the interface.

Image

The algorithm I looking for has to reorder all the products if the user changes the input of the Orden column.

For example:

  1. The user sets the input Orden of the product 0003-0020 to 11 and clicks on the Guardar button.
  2. Then, the algorithm has to change the position of this product and put it second on the list. Updating its orden input value to 20.
  3. The algorithm has to update all the following products, increasing their positions +10.

If the user sets a value that already exists, the product will place below the product that already has the position entered. The position values are always multiple of 10.

The data is structured in an ArrayList that contains the product reference and its position: Its name is lista_variantes.

0: {numero: "0003-0007", posicion: 10}
1: {numero: "0003-0006", posicion: 20}
2: {numero: "0003-0004", posicion: 30}
3: {numero: "0003-0008", posicion: 40}
4: {numero: "0003-0014", posicion: 50}
5: {numero: "0003-0016", posicion: 60}
6: {numero: "0003-0017", posicion: 70}
7: {numero: "0003-0018", posicion: 80}

And then using this map, I update the Database and read the product information in the correct order. I have implemented correctly this, I only want the algorithm to achieve the example behaviour.

1 Answers1

2
  • You could map the array of objects and add a new property called isUpdated which checks if the posicion is same based on the index.
  • Then, sort the array based on posicion. If 2 items have the same posicion, then sort them based on isUpdated property
  • Then map the array again to remove the isUpdated property and get the posicion values based on their index

const map = [
    { numero: "0003-0007", posicion: 10 },
    { numero: "0003-0006", posicion: 20 },
    { numero: "0003-0004", posicion: 30 },
    { numero: "0003-0008", posicion: 25 }, //-- updated
    { numero: "0003-0014", posicion: 20 }, //-- updated
    { numero: "0003-0016", posicion: 60 }
]

const output = map.map((o, i) => ({ ...o, isUpdated: (i+1) * 10 !== o.posicion }))
                  .sort((a,b) => a.posicion - b.posicion || a.isUpdated - b.isUpdated)
                  .map(({ isUpdated, ...rest }, i) => ({ ...rest, posicion: (i+1) * 10 }))

console.log(output)

It would be better if you have another property to keep the posicion value before update or an onchange event which updates the isUpdated proeprty. If you have that, you can skip the step where you check whether the object has been updated or not.

Here's a snippet using for loop and some comments

const map = [
    { numero: "0003-0007", posicion: 10 },
    { numero: "0003-0006", posicion: 20 },
    { numero: "0003-0004", posicion: 30 },
    { numero: "0003-0008", posicion: 25 }, //-- updated
    { numero: "0003-0014", posicion: 20 }, //-- updated
    { numero: "0003-0016", posicion: 60 }
]

// add isUpdated property and set it to true if posicion is not equal to (i+1) * 10
for (let i = 0; i < map.length; i++) {
  const o = map[i];
  o.isUpdated = (i+1) * 10 !== o.posicion
}

// sort based on posicion
// if both have the same posicion, the subtraction returns 0
// so, || will check the next condition
// Sorting based on boolean: https://stackoverflow.com/a/54826593
map.sort((a,b) => a.posicion - b.posicion || a.isUpdated - b.isUpdated);

// delete the isUpdated property
// update the posicion based on the new index
for (let i = 0; i < map.length; i++) {
  const o = map[i];
  o.posicion = (i+1) * 10;
  delete o.isUpdated
}

console.log(map)
adiga
  • 34,372
  • 9
  • 61
  • 83
  • How would you do it to add the isUpdated boolean? – Emili Bellot Pulido Apr 26 '21 at 14:28
  • I didn't understand your question – adiga Apr 26 '21 at 14:39
  • What you said at the end of your answare. – Emili Bellot Pulido Apr 26 '21 at 14:42
  • 1
    If you are using some 2-way binding framework, you could have 2 separate properties for `posicion` both have the same value when this component loads and one of them bound to that input data and changes when the user types there. Then isUpdated can be computed with `initialPosicion !== o.inputPosicion`. Or, an `onChange` event on the input. When the value changes check if the value entered is different than `initialPosicion` value or it's different than `(i+1) * 10`, set `isUpdated = true` – adiga Apr 26 '21 at 15:22
  • Please, can you explain the code of the output map? – Emili Bellot Pulido Apr 27 '21 at 06:38
  • I have tried what you say, but I don't understand really well what your code does... `const newOrder = list.map((o, i) => ({ ...o, isUpdated: (i+1) * 10 !== o.initialPosition })) .sort((a,b) => a.position - b.position || a.isUpdated - b.isUpdated) .map(({ isUpdated, ...rest }, i) => ({ ...rest, initialPosition, position: (i+1) * 10 })); return newOrder;` – Emili Bellot Pulido Apr 27 '21 at 07:12
  • @EmiliBellotPulido The last `map` uses rest syntax in [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#rest_in_object_destructuring) to remove `isUpdated` and move all the remaining properties to a `rest` variable. I've added another snippet which uses `for` loop and does each step one at a time. – adiga Apr 27 '21 at 07:24
  • Okey, so, if I want to add the initialPosition attribute, is better to use the `for loop`? – Emili Bellot Pulido Apr 27 '21 at 07:31
  • I have resolved. I read the link of destructing and I have understood the code. Thanks. – Emili Bellot Pulido Apr 27 '21 at 07:41