Vue/Javascript – Sort array of objects based on its existence in another array

Tags: , , , ,



I have two arrays, array1 has all objects and array2 has filtered objects based on a search string.

Currently I am rendering array2 (which will contain all objects from array1 when the search string is empty, but will return only filtered objects if search string isn’t empty) to the user but I would like to show all objects and style the filtered ones (those which match the search) differently and keep those matching the search query in the top of the array/list and if I even in addition to that could also sort those matched objects alphabetically I would love to.

Here is how I am filtering based on the search query:

export default {
    name: "RegionSelector",
  
    data: () => ({
      searchRegionTextValue: "",
  
      regions: [
        {
          country: "USA",
          flag: "flag-en-us",
          name: "United States",
          language: "English",
        },
        {
          country: "UK",
          flag: "flag-en-gb",
          name: "United Kingdom",
          language: "English",
        },
        {
          country: "DE",
          flag: "flag-de",
          name: "Germany",
          language: " German",
        },
      ],
    }),
  
    methods: {
      // Used in my v-for in the template to style non-matched results differently
      checkRegion(region) {
        var isInArray =
          this.filteredRegions.find(function(el) {
            return el === region;
          }) !== undefined;
        return isInArray;
      },
  
    computed: {
      filteredRegions() {
        function compare(a, b) {
          if (a.name < b.name) return -1;
          if (a.name > b.name) return 1;
  
          return 0;
        }
  
        let regions = this.regions.filter((region) => {
          return (
            region.name
              .toLowerCase()
              .indexOf(this.searchRegionTextValue.toLowerCase()) != -1 ||
            region.language
              .toLowerCase()
              .indexOf(this.searchRegionTextValue.toLowerCase()) != -1
          );
        });
  
        regions.sort(compare);
  
        return regions;
      },
    },
};

And in my template I am rendering them this way (to show all objects but style them differently):

<div v-for="region in regions">
    <span
    :class="checkRegion(region) ? 'matched-query' : 'unmatched-query'">
        {{region.name}}
    </span>
</div>

How can I implement the sorting as mentioned above?

Answer

Use the spread operator to append the original array to the result of the filtered array like this

[...filteredRegions(), ...regions]

This adds the matches to the beginning of the array, next we remove duplicates from the array, and we can do that just by wrapping the new Set() around it, just like this

const newRegions = new Set([...filteredRegions(), ...regions])

You can convert it to a plain array just like this.

const newRegions = [...new Set([...filteredRegions(), ...regions])]

See Example code below:

const regions = [
  {
    country: "USA",
    flag: "flag-en-us",
    name: "United States",
    language: "English",
  },
  {
    country: "UK",
    flag: "flag-en-gb",
    name: "United Kingdom",
    language: "English",
  },
  {
    country: "DE",
    flag: "flag-de",
    name: "Germany",
    language: " German",
  },
  {
    country: "NG",
    flag: "flag-ng",
    name: "Nigeria",
    language: "English",
  },
]

function compare(a, b) {
  if (a.name < b.name) return -1;
  if (a.name > b.name) return 1;

  return 0;
}

function filteredRegions(query = 'Nigeria') {
  let regions = regions.filter((region) => {
    return (
      region.name
        .toLowerCase()
        .indexOf(query.toLowerCase()) != -1 || region.language
        .toLowerCase()
        .indexOf(query.toLowerCase()) != -1
    );
  });

  regions.sort(compare);

  return regions;
}

let result = [...new Set([...filteredRegions(), ...regions])]


Source: stackoverflow