简体   繁体   English

使用下划线针对多个值过滤/拒绝字符串数组

[英]Filter/Reject Array of strings against multiple values using underscore

I'd like to _.filter or _.reject the cities array using the filters array using underscore. 我想使用下划线使用filters数组来_.filter_.reject cities数组。

var cities = ['USA/Aberdeen', 'USA/Abilene', 'USA/Akron', 'USA/Albany', 'USA/Albuquerque', 'China/Guangzhou', 'China/Fuzhou', 'China/Beijing', 'China/Baotou', 'China/Hohhot' ... ]
var filters = ['Akron', 'Albuquerque', 'Fuzhou', 'Baotou'];

My progress so far: 到目前为止,我的进度:

var filterList;

if (reject) {
    filterList = angular.copy(cities);
    _.each(filters, (filter) => {
        filterList = _.reject(filterList, (city) => city.indexOf(filter) !== -1);
    });
} else {
    filterList = [];
    _.each(filters, (filter) => {
        filterList.push(_.filter(cities, (city) => city.indexOf(filter) !== -1));
    });
}

filterList = _.flatten(filterList);

return filterList;

I'd like to DRY this up and use a more functional approach to achieve this if possible? 我想对此进行干燥,并在可能的情况下使用更具功能性的方法来实现这一目标?

A somewhat more functional version using Underscore might look like this: 使用Underscore的更具功能性的版本可能如下所示:

const cities = ['USA/Aberdeen', 'USA/Abilene', 'USA/Akron', 'USA/Albany', 
                'USA/Albuquerque', 'China/Guangzhou', 'China/Fuzhou',
                'China/Beijing', 'China/Baotou', 'China/Hohhot']
const filters = ['Akron', 'Albuquerque', 'Fuzhou', 'Baotou'];

var inList = names => value => _.any(names, name => value.indexOf(name) > -1);

_.filter(cities, inList(filters));
//=> ["USA/Akron", "USA/Albuquerque", "China/Fuzhou", "China/Baotou"]

_.reject(cities, inList(filters));
//=> ["USA/Aberdeen", "USA/Abilene", "USA/Albany", 
//    "China/Guangzhou", "China/Beijing", "China/Hohhot"]

I'm using vanilla JavaScript here (some() and filter()) but I hope you get the idea: 我在这里使用香草JavaScript(some()和filter()),但我希望你能明白:

const isValidCity = city => filters.some(filter => city.indexOf(filter) > -1)

const filteredCities = cities.filter(isValidCity)

Please note that this is a loop over a loop. 请注意,这是一个循环。 So the time complexity is O(n * m) here. 因此,这里的时间复杂度为O(n * m)。

In your example all city keys share the same pattern: country + / + city . 在您的示例中,所有城市键共享相同的模式: country + / + city Your filters are all an exact match to the city part of these names. 您的过滤器均与这些名称的city部分完全匹配。

If this is a certainty in your data (which it probably isn't...) , you could reduce the number of loops your code makes by creating a Map or object that stores each city per filter entry: 如果这是数据中的确定性(可能不是...) ,则可以通过创建一个Mapobject来存储每个过滤器条目的每个城市,从而减少代码的循环次数:

  • Create an object with an entry for each city name 创建一个带有每个城市名称的条目的对象
  • Make the key the part that you want the filter to match 使key成为您希望过滤器匹配的部分
  • Make the value the original name value设为原始名称
  • Loop through the filters and return the name at each key. 循环浏览filters并在每个键处返回名称。

This approach always requires one loop through the data and one loop through the filters. 这种方法总是需要一个数据循环和一个过滤器循环。 For small array sizes, you won't notice a performance difference. 对于较小的阵列大小,您不会注意到性能差异。 When one of the arrays has length 1, you'll also not notice any differences. 当其中一个数组的长度为1时,您也不会注意到任何差异。

Again, note that this only works if there's a constant relation between your filters and cities. 同样,请注意,这仅在过滤条件和城市之间存在恒定关系时才有效。

 var cities = ['USA/Aberdeen', 'USA/Abilene', 'USA/Akron', 'USA/Albany', 'USA/Albuquerque', 'China/Guangzhou', 'China/Fuzhou', 'China/Beijing', 'China/Baotou', 'China/Hohhot' ] var filters = ['Akron', 'Albuquerque', 'Fuzhou', 'Baotou']; const makeMap = (arr, getKey) => arr.reduce( (map, x) => Object.assign(map, { [getKey(x)]: x }), {} ); const getProp = obj => k => obj[k]; const getKeys = (obj, keys) => keys.map(getProp(obj)); // Takes the part after the "/" const cityKey = c => c.match(/\\/(.*)/)[1]; const cityMap = makeMap(cities, cityKey); const results = getKeys(cityMap, filters); console.log(results); 

Since you seem to be using AngularJS, you could utilize the built-in filter functionality. 由于您似乎正在使用AngularJS,因此可以利用内置的过滤器功能。 Assuming both the cities and filters array exist on your controller and you're displaying the cities array using ng-repeat , you could have something like this on your controller: 假设city和filter数组同时存在于您的控制器上,并且您正在使用ng-repeat显示city数组,那么您的控制器上可能会有类似的内容:

function cityFilter(city) {
    var cityName = city.split('/')[1];
    if (reject) {
        return filters.indexOf(cityName) === -1;
    } else {
        return filters.indexOf(cityName) > -1;
    }
}

And then in your template, you'd do something like this: 然后在模板中,您将执行以下操作:

<div ng-repeat="city in cities | filter : cityFilter"></div>

Of course you'd have to modify your syntax a bit depending on your code style (for example, whether you use $scope or controllerAs ). 当然,您必须根据代码样式(例如,使用$scope还是controllerAs )来稍微修改语法。

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

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