1

I have an array of objects like this (continents):

data() {
    return {
        continents: [
            {
                name: "South America",
                countries: [
                    {
                        name: "Paraguay"
                    },
                    {
                        name: "Chile"
                    }
                ]
            },
            {
                name: "North America",
                countries: [
                    {
                        name: "Costa Rica"
                    },
                    {
                        name: "Mexico"
                    }
                ]
            }
        ]
    }
}


// 6 continents and over 250 countries within.

And Im trying to filter it with v-model 'filter' like this

computed: {
    filtered() {
        return this.continents.filter(continent => {
            continent.countries = continent.countries.filter(country => {
                return country.name.match(new RegExp(this.filter, 'i'));
            });
            return continent.countries.length;
        });
    }
}

And render it with v-for directive like this:

<input v-model="filter" type="text">
<div v-for="continent in filtered" v-if="filtered.length" class="countries-group">
    <h4>{{ continent.name }}</h4>
    <ul class="country-list">
        <li v-for="country in continent.countries" class="country-item">{{ country.name }}</li>
    </ul>
</div>

And it almost works, but my computed property filtered modifies the original countries data, so when i'm trying to backspace filter v-model, it not return the inited data because its already overriding with filtered objects.

1 Answer 1

2

my computed property filtered modifies the original contries data

You shouldn't do this. Don't modify state within computed properties because it may trigger other computed properties to be re-evaluated and cause confusing bugs. Computed properties are meant to be pure functions which transforms (without mutating) the component's state in some way.

Try this:

filtered() {
  const filter = this.filter.toLowerCase();

  return this.continents
    .map(continent => Object.assign({}, continent, {
      countries: continent.countries.filter(country => {
        return country.name.toLowerCase().includes(filter);
      }),
    })
    .filter(continent => continent.countries.length);
}
Sign up to request clarification or add additional context in comments.

3 Comments

oh, thank you. that's working. but we should swap filter and map calls, i guess
It wouldn't make any difference. It's better performance to reduce the size of the array first (filter()) before doing the map().
but that allows us to avoid rendering continents with empty countries

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.