taravasya_github
taravasya_github

Reputation: 124

Jquery loop and strange behavior of variables

I'm sorry, but I can't find better title for my question, but I'll try to better describe my problem. I have this code:

let pdfInvoice_sub_template = [
        {text: '{i}', alignment: 'center'},
        {text: 'invoices_sub-{i}-name', alignment: 'left'},
        {text: 'invoices_sub-{i}-unit', alignment: 'center'},
    ];
temp_pdfSubTemplate = [];

for (i = 1; i <= 2; i++) {
        temp_pdfSubTemplate[i] = pdfInvoice_sub_template;
        jQuery.each(temp_pdfSubTemplate[i], function (key, val) {
                 document.write(val.text = val.text.replace(/{i}/g, i) + '</br>');
        });
}

And hoping add two elements to the object, like this:

  temp_pdfSubTemplate [[{ text: '1', alignment: 'center' },{ text: 'invoices_sub-1-name', alignment: 'left' },{ text: 'invoices_sub-1-unit', alignment: 'center' }], [{ text: '2', alignment: 'center' },{ text: 'invoices_sub-2-name', alignment: 'left' },{ text: 'invoices_sub-2-unit', alignment: 'center' }]]

But after run this code, I have:

1
invoices_sub-1-name
invoices_sub-1-unit

1
invoices_sub-1-name
invoices_sub-1-unit

In debug devtools I see after first iterate that pdfInvoice_sub_template have a replaced {i} with 1, but I do replace {i} in temp_pdfSubTemplate. Why is happened this way and what i must to do?
Thanks!

Here is fiddler:
https://jsfiddle.net/xf4j1qpu/1/


Thanks to @Lennholm, who suggested the correct answer (see below), the problem was solved. The main reason is that when I declaring temp_pdfSubTemplate[i] in a loop, I do not create a new object, but refer to the original object pdfInvoice_sub_template, and as a result, manipulations in the loop are performed on the original object.
The problem has two solutions.
In addition, as suggested by @Lennholm, instead of declaring a variable with the original object, create a function whose only task is to create the object I need(see example in @Lennholm answear). Thus, when creating a variable in a loop, it will not just be a reference to the original object, but a new one will be created.

As a second solution is to move the declarations of the original object to the beginning of the loop. Thus, the desired result is also achieved. In each new loop, a new object will be created:

temp_pdfSubTemplate = [];

for (i = 1; i <= 2; i++) {
let pdfInvoice_sub_template = [
        {text: '{i}', alignment: 'center'},
        {text: 'invoices_sub-{i}-name', alignment: 'left'},
        {text: 'invoices_sub-{i}-unit', alignment: 'center'},
    ];
        temp_pdfSubTemplate[i] = pdfInvoice_sub_template;
        jQuery.each(temp_pdfSubTemplate[i], function (key, val) {
                 document.write(val.text = val.text.replace(/{i}/g, i) + '</br>');
        });
}

Upvotes: 1

Views: 54

Answers (1)

Lennholm
Lennholm

Reputation: 7470

pdfInvoice_sub_template and temp_pdfSubTemplate[i] point to the same array. When you run temp_pdfSubTemplate[i] = pdfInvoice_sub_template you're only copying the reference to the original array, you're not creating a copy of the array itself.
That means that whenever you change something in temp_pdfSubTemplate[i], that change will appear in pdfInvoice_sub_template as well since they are the same thing.

Hence, when you do val.text = ... you're overwriting the text property in the original template. In the next iteration, it will have 1 instead of {i} so the replacement fails and 1 remains.

Since you want temp_pdfSubTemplate[i] to be a new, unique array based on the template, my suggestion is that you make a factory function that creates this for you instead of having a predefined object.
Change your code like this:

function createPdfInvoice_sub_template() {
      return [
        {text: '{i}', alignment: 'center'},
        {text: 'invoices_sub-{i}-name', alignment: 'left'},
        {text: 'invoices_sub-{i}-unit', alignment: 'center'},
      ];
    }

    temp_pdfSubTemplate = [];

    for (i = 1; i <= 2; i++) {
        temp_pdfSubTemplate[i] = createPdfInvoice_sub_template();
        jQuery.each(temp_pdfSubTemplate[i], function (key, val) {
                 document.write(val.text = val.text.replace(/{i}/g, i) + '</br>');
        });
     }
     console.log(temp_pdfSubTemplate);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Upvotes: 2

Related Questions