简体   繁体   English

Vue / Javascript - 根据对象在另一个数组中的存在对对象数组进行排序

[英]Vue/Javascript - Sort array of objects based on its existence in another array

I have two arrays, array1 has all objects and array2 has filtered objects based on a search string.我有两个 arrays,array1 包含所有对象,array2 包含基于搜索字符串的过滤对象。

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.目前我正在向用户渲染array2(当搜索字符串为空时,它将包含来自array1的所有对象,但如果搜索字符串不为空,则仅返回过滤的对象)但我想显示所有对象并设置过滤后的对象(那些与搜索匹配的)以不同的方式保留那些匹配搜索查询的数组/列表的顶部,如果我什至除此之外还可以按字母顺序对那些匹配的对象进行排序,我会喜欢。

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?如何实现上述排序?

Use the spread operator to append the original array to the result of the filtered array like this使用扩展运算符将 append 原始数组转换为过滤数组的结果,如下所示

[...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这会将匹配项添加到数组的开头,接下来我们从数组中删除重复项,我们可以通过将new Set()包裹在它周围来做到这一点,就像这样

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])]

You can introduce a second computed method which contain the un-matched objects and do another for loop for that array您可以引入第二个包含不匹配对象的computed方法,并为该数组执行另一个for loop

Or或者

'might be' more efficiently, introduce another property to flag the object as matched object, and also use that property for the sorting logic “可能”更有效,引入另一个属性将 object 标记为匹配的 object,并将该属性用于排序逻辑

computed: {
  newregions() {
    function compare(a, b) {
      if (a.matched && !b.matched) return -1;
      if (!a.matched && b.matched) return 1;
      if (a.matched && b.matched) {
        if (a.name < b.name) return -1;
        if (a.name > b.name) return 1;
      }

      return 0;
    }

    let regions = this.regions.map((region) => ({
      ...region,
      matched: region.name
          .toLowerCase()
          .indexOf(this.searchRegionTextValue.toLowerCase()) != -1 ||
        region.language
          .toLowerCase()
          .indexOf(this.searchRegionTextValue.toLowerCase()) != -1
    }));

    regions.sort(compare);

    return regions;
  },
},

and then on your template然后在你的模板上

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM