Filter/Reject 使用下划线针对多个值的字符串数组
Filter/Reject Array of strings against multiple values using underscore
我想 _.filter
或 _.reject
cities
数组使用下划线 filters
数组。
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'];
目前我的进度:
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;
如果可能的话,我想把它擦干并使用更实用的方法来实现它?
我在这里使用香草 JavaScript(some() 和 filter()),但我希望你能理解:
const isValidCity = city => filters.some(filter => city.indexOf(filter) > -1)
const filteredCities = cities.filter(isValidCity)
请注意,这是一个循环之上的循环。所以这里的时间复杂度是O(n * m)。
由于您似乎在使用 AngularJS,您可以利用内置的过滤器功能。假设 cities 和 filters 数组都存在于你的控制器上,并且你正在使用 ng-repeat
显示 cities 数组,你可以在你的控制器上有这样的东西:
function cityFilter(city) {
var cityName = city.split('/')[1];
if (reject) {
return filters.indexOf(cityName) === -1;
} else {
return filters.indexOf(cityName) > -1;
}
}
然后在你的模板中,你会做这样的事情:
<div ng-repeat="city in cities | filter : cityFilter"></div>
当然,您必须根据自己的代码风格(例如,使用 $scope
还是 controllerAs
)稍微修改一下语法。
使用 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"]
在您的示例中,所有城市键共享相同的模式:country
+ /
+ city
。您的过滤器与这些名称的 city
部分完全匹配。
如果这在您的数据中是确定的 (可能不是...),您可以通过创建 Map
或 object
存储每个过滤器条目的每个城市:
- 为每个城市名称创建一个包含条目的对象
- 使
key
成为您希望过滤器匹配的部分
- 将
value
改成原来的名字
- 遍历每个键的
filters
和 return 名称。
这种方法总是需要 one 循环遍历数据和 one 循环遍历过滤器。对于小数组大小,您不会注意到性能差异。当其中一个数组的长度为 1 时,您也不会注意到任何差异。
再次注意,这仅在您的过滤器和城市之间存在恒定关系时才有效。
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);
我想 _.filter
或 _.reject
cities
数组使用下划线 filters
数组。
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'];
目前我的进度:
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;
如果可能的话,我想把它擦干并使用更实用的方法来实现它?
我在这里使用香草 JavaScript(some() 和 filter()),但我希望你能理解:
const isValidCity = city => filters.some(filter => city.indexOf(filter) > -1)
const filteredCities = cities.filter(isValidCity)
请注意,这是一个循环之上的循环。所以这里的时间复杂度是O(n * m)。
由于您似乎在使用 AngularJS,您可以利用内置的过滤器功能。假设 cities 和 filters 数组都存在于你的控制器上,并且你正在使用 ng-repeat
显示 cities 数组,你可以在你的控制器上有这样的东西:
function cityFilter(city) {
var cityName = city.split('/')[1];
if (reject) {
return filters.indexOf(cityName) === -1;
} else {
return filters.indexOf(cityName) > -1;
}
}
然后在你的模板中,你会做这样的事情:
<div ng-repeat="city in cities | filter : cityFilter"></div>
当然,您必须根据自己的代码风格(例如,使用 $scope
还是 controllerAs
)稍微修改一下语法。
使用 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"]
在您的示例中,所有城市键共享相同的模式:country
+ /
+ city
。您的过滤器与这些名称的 city
部分完全匹配。
如果这在您的数据中是确定的 (可能不是...),您可以通过创建 Map
或 object
存储每个过滤器条目的每个城市:
- 为每个城市名称创建一个包含条目的对象
- 使
key
成为您希望过滤器匹配的部分 - 将
value
改成原来的名字 - 遍历每个键的
filters
和 return 名称。
这种方法总是需要 one 循环遍历数据和 one 循环遍历过滤器。对于小数组大小,您不会注意到性能差异。当其中一个数组的长度为 1 时,您也不会注意到任何差异。
再次注意,这仅在您的过滤器和城市之间存在恒定关系时才有效。
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);