Défi

Émilie possède une collection de livres qu'elle souhaite vendre à un libraire en janvier. Elle dispose du prix d'achat de chacun des livres ainsi que d'une estimation du prix de vente sous la forme d'un tableau :

Titre livre

Prix d'achat (en €)

Prix de vente estimé (en €)

Les Fleurs du Mal

7.00

7.00

La Guerre des Intelligences

20.90

1.23

Inferno

9.99

5.30

Question

Elle a réalisé un programme en JavaScript dont la logique amène à l'état suivant :

1
const bookListJanuaryEstimations = 
2
[
3
  ['Les Fleurs du Mal', 7.00, 7.00],
4
  ['La Guerre des Intelligences', 20.90, 1.23],
5
  ['Inferno', 9.99, 5.30]
6
]
7
8
let totalCash = 0
9
10
for (const book of bookListJanuaryEstimations) {
11
  totalCash = totalCash + book[2]
12
}
13
14
console.log('Estimation de la vente en Janvier:', totalCash.toFixed(2), '€')

Combien d'argent peut-elle espérer gagner en Janvier avec la présente liste de livres ?

Solution

On exécute le programme et on obtient :

1
Estimation de la vente en Janvier: 13.53 €

Question

Afin de rendre la logique de calcul plus générale, Émilie décide de modifier le programme pour réaliser l'estimation de ses gains dans une fonction dédiée  :

1
const bookListJanuaryEstimations = 
2
[
3
  ['Les Fleurs du Mal', 7.00, 7.00],
4
  ['La Guerre des Intelligences', 20.90, 1.23],
5
  ['Inferno', 9.99, 5.30]
6
]
7
8
function estimateCashOnSell(totalCash, bookList) {
9
  for (const book of bookList) {
10
    totalCash = totalCash + book[2]
11
  }
12
}
13
14
let totalCash = 0
15
16
estimateCashOnSell(totalCash, bookListJanuaryEstimations)
17
18
console.log('Estimation de la vente en Janvier:', totalCash.toFixed(2), '€')

Néanmoins elle pense avoir réalisé une erreur. Quel est le résultat retourné par le programme ?

Pourquoi obtient-on ce résultat ?

Proposer une correction à apporter au programme pour corriger ce problème et vérifier qu'on obtient le même résultat que précédemment.

Solution

On obtient :

1
Estimation de la vente en Janvier: 0.00 €

On obtient ce résultat car totalCash est passé par valeur : les ajouts qui sont réalisés n'influencent pas la valeur de la variable totalCash dans l'étendue globale.

Pour corriger ce problème, on peut corriger la fonction pour simplement retourner le montant total estimé de la vente et réaliser la somme avec totalCash ainsi :

1
const bookListJanuaryEstimations = 
2
[
3
  ['Les Fleurs du Mal', 7.00, 7.00],
4
  ['La Guerre des Intelligences', 20.90, 1.23],
5
  ['Inferno', 9.99, 5.30]
6
]
7
8
function estimateCashOnSell(bookList) {
9
  let amountEstimate = 0
10
  for (const book of bookList) {
11
    amountEstimate = amountEstimate + book[2]
12
  }
13
  return amountEstimate
14
}
15
16
let totalCash = 0
17
18
totalCash = estimateCashOnSell(bookListJanuaryEstimations)
19
20
console.log('Estimation de la vente en Janvier:', totalCash.toFixed(2), '€')

On obtient alors le résultat attendu, à savoir :

1
Estimation de la vente en Janvier: 13.53 €

Question

Émilie dispose maintenant d'estimation de la vente de ces livres pour le mois de février.

Titre livre

Prix d'achat (en €)

Prix de vente estimé (en €)

Les Fleurs du Mal

7.00

8.23

La Guerre des Intelligences

20.90

0.52

Inferno

9.99

5.30

Puisque peu de données changent, elle modifie son programme à la marge pour calculer l'estimation de ces gains sur ce nouveau mois ainsi :

1
const bookListJanuaryEstimations = 
2
[
3
  ['Les Fleurs du Mal', 7.00, 7.00],
4
  ['La Guerre des Intelligences', 20.90, 1.23],
5
  ['Inferno', 9.99, 5.30]
6
]
7
8
bookListFebruaryEstimations = bookListJanuaryEstimations
9
bookListFebruaryEstimations[0][2] = 8.23
10
bookListFebruaryEstimations[1][2] = 0.52
11
12
function estimateCashOnSell(bookList) {
13
  let amountEstimate = 0
14
  for (const book of bookList) {
15
    amountEstimate = amountEstimate + book[2]
16
  }
17
  return amountEstimate
18
}
19
20
21
const totalCashJanuary = estimateCashOnSell(bookListJanuaryEstimations)
22
console.log('Estimation de la vente en Janvier:', totalCashJanuary.toFixed(2), '€')
23
24
const totalCashFebruary = estimateCashOnSell(bookListFebruaryEstimations)
25
console.log('Estimation de la vente en Février:', totalCashFebruary.toFixed(2), '€')

Néanmoins, elle trouve des résultats différents pour le mois de Janvier.

Quel est le résultat de l'exécution de ce programme ?

Pourquoi obtient-on un résultat différent sur le mois de Janvier comparé à précédemment ?

Solution

Le résultat de l'exécution de ce programme est :

1
Estimation de la vente en Janvier: 14.05 €
2
Estimation de la vente en Février: 14.05 €

On obtient un résultat différent pour janvier car on a en réalité partagé une valeur de type composé (le tableau) entre les deux variables bookListJanuaryEstimations et bookListFebruaryEstimations. Ainsi les modifications faites via bookListFebruaryEstimations sont rendues visibles à partir de bookListJanuaryEstimations, d'où un résultat identique pour les deux mois et qui se base sur les prix estimés pour Février.

Question

En relisant son code, elle s'est rendu compte qu'il s'agissait d'un problème de copie de tableaux. Elle modifie donc son code ainsi pour réaliser une copie avec la méthode Object.assign.

1
const bookListJanuaryEstimations = 
2
[
3
  ['Les Fleurs du Mal', 7.00, 7.00],
4
  ['La Guerre des Intelligences', 20.90, 1.23],
5
  ['Inferno', 9.99, 5.30]
6
]
7
8
bookListFebruaryEstimations = Object.assign([], bookListJanuaryEstimations)
9
bookListFebruaryEstimations[0][2] = 8.23
10
bookListFebruaryEstimations[1][2] = 0.52
11
12
function estimateCashOnSell(bookList) {
13
  let amountEstimate = 0
14
  for (const book of bookList) {
15
    amountEstimate = amountEstimate + book[2]
16
  }
17
  return amountEstimate
18
}
19
20
21
const totalCashJanuary = estimateCashOnSell(bookListJanuaryEstimations)
22
console.log('Estimation de la vente en Janvier:', totalCashJanuary.toFixed(2), '€')
23
24
const totalCashFebruary = estimateCashOnSell(bookListFebruaryEstimations)
25
console.log('Estimation de la vente en Février:', totalCashFebruary.toFixed(2), '€')

Le résultat persiste-t-il ?

Pourquoi ? S'il persiste, comment peut-on résoudre ce problème ? Modifier le code en conséquence.

Indice

On pourra utiliser la fonction de copie récursive suivante :

1
const deepCopy = (items) => items.map(item => Array.isArray(item) ? deepCopy(item) : item)

Solution

Le résultat persiste, on obtient toujours :

1
Estimation de la vente en Janvier: 14.05 €
2
Estimation de la vente en Février: 14.05 €

Solution

La méthode Object.assign ne réalise qu'une copie superficielle du tableau : ainsi, les valeurs contenues dans les tableaux imbriqués ne sont pas recopiées et sont partagées entre les deux variables, et on rencontre le même problème que précédemment. Pour résoudre ce problème, c'est-à-dire pour recopier toutes les valeurs, il faut utiliser une copie récursive.

1
const deepCopy = (items) => items.map(item => Array.isArray(item) ? deepCopy(item) : item)
2
3
const bookListJanuaryEstimations = 
4
[
5
  ['Les Fleurs du Mal', 7.00, 7.00],
6
  ['La Guerre des Intelligences', 20.90, 1.23],
7
  ['Inferno', 9.99, 5.30]
8
]
9
10
bookListFebruaryEstimations = deepCopy(bookListJanuaryEstimations)
11
bookListFebruaryEstimations[0][2] = 8.23
12
bookListFebruaryEstimations[1][2] = 0.52
13
14
15
function estimateCashOnSell(bookList) {
16
  let amountEstimate = 0
17
  for (const book of bookList) {
18
    amountEstimate = amountEstimate + book[2]
19
  }
20
  return amountEstimate
21
}
22
23
24
const totalCashJanuary = estimateCashOnSell(bookListJanuaryEstimations)
25
console.log('Estimation de la vente en Janvier:', totalCashJanuary.toFixed(2), '€')
26
27
const totalCashFebruary = estimateCashOnSell(bookListFebruaryEstimations)
28
console.log('Estimation de la vente en Février:', totalCashFebruary.toFixed(2), '€')

On obtient bien le résultat souhaité :

1
Estimation de la vente en Janvier: 13.53 €
2
Estimation de la vente en Février: 14.05 €