Tengo una API que sirve datos JSON. Actualmente, si haces api/weapons por ejemplo, te da todas las armas disponibles, api/weapons/weaponName da información sobre esa arma específica. Lo que quiero hacer es poder api/weapons?type=sword&rarity=5 por ejemplo. Me las arreglé para sacar api/weapons?type=sword y api/weapons?rarity=5 solos pero no juntos.
Esto es lo que estoy haciendo actualmente:
let filtered = []; if (query.type) { filtered = filtered.concat((await weapons).filter(w => formatName(w.weaponType) === formatName(query.type))); } if (query.rarity) { filtered = filtered.concat((await weapons).filter(w => w.rarity == query.rarity)); } if (!filtered.length) filtered = [await weapons] res.status(HttpStatusCodes.ACCEPTED).send(filtered);
formatNamees solo una función que hace que la cadena esté en minúsculas y la recorta y elimina todos los espacios.
Si tomamos api/weapons?type=sword&rarity=5 , creo que lo que está sucediendo ahora mismo es:
Quiero que filtre armas con SOLO esa rareza Y SOLO ese tipo. Entonces, solo 5 espadas de rareza, por ejemplo. ¿Cuál es la forma más beneficiosa de manejar esto?
Sugeriría recuperar "armas" una vez y luego ejecutar cualquier filtro en ellas sin concatenar los resultados:
let filtered = [ ...(await weapons) ]; if (query.type) { filtered = filtered.filter(w => w => formatName(w.weaponType) === formatName(query.type)); } if (query.rarity) { filtered = filtered.filter(w => w.rarity == query.rarity); } res.status(HttpStatusCodes.ACCEPTED).send(filtered);Su lógica actual está probando si una restricción U otra coincide, lo que realmente necesita hacer es hacer un AND , lo que significa que debe realizar la prueba en un solo paso de filter .
Modificaría ligeramente su código para que compare todas las restricciones que está enviando... podría modificar aún más la lógica a continuación para aceptar un operador lógico para probar si la rareza es >= o <= a un número determinado, por ejemplo.
const weapons = [{ type: 'sword', name: 'swift blade of zek', rarity: 5 }, { type: 'mace', name: 'hammer of kromzek kings', rarity: 1 }, { type: 'sword', name: 'split blade of thunder', rarity: 2 }, { type: 'sword', name: 'blade of carnage', rarity: 5 }, ] const getWeapons = (query = {}) => { let filtered = []; let constraints = []; // We could build this object dynamically but I just wanted // to demonstrate it using your current approach if (query.hasOwnProperty('type')) { constraints.push({ name: 'type', value: query.type }) } if (query.hasOwnProperty('rarity')) { constraints.push({ name: 'rarity', value: query.rarity }) } // Compare all of the conditions and only return weapons // that match all of the conditions passed. filtered = weapons.filter(w => { let matches = 0 constraints.forEach(c => { if (w[c.name] === c.value) { matches += 1 } }) // ensures we only return complete matches return matches === constraints.length }); return filtered } console.log(getWeapons({ type: 'sword', rarity: 5 }))Cree un objeto que tenga las mismas claves de propiedad que los filtros que desea utilizar. Asigne una función a cada propiedad donde se especifica la evaluación para ese filtro específico.
const filters = { type: (weapon, type) => formatName(weapon.weaponType) === formatName(type), rarity: (weapon, rarity) => weapon.rarity === rarity, }; Luego pase las armas con filter . Dentro del bucle de filtro, recorra las claves de la variable de query con every método. Este método devolverá verdadero o falso en función de si cada evaluación es verdadera o no.
En cada ciclo, use las teclas de la query para seleccionar el filtro de la lista de filters . Pase el weapon y los valores del objeto de query a estas funciones de filtro y devuelva el resultado.
Al hacer esto, puede usar uno, dos o ningún filtro. Y cualquier filtro nuevo se puede agregar en el objeto de filters .
const filteredWeapons = weapons.filter((weapon) => Object.keys(query).every((filterKey) => { if (!(filterKey in filters)) { return false; } const filter = filters[filterKey] const value = query[filterKey] return filter(weapon, value); }) ); res.status(HttpStatusCodes.ACCEPTED).send(filteredWeapons);