So, I have this array of objects called "characters" (currently only 3 objects, but it's supposed to be much more) and I want to filter through this array with a function (or an array method) and for example: return every character's name, who is character.bodyType: "tall"
or has character.element: "fire"
AND is also character.weapon: "sword"
user, based on how many parameter values I pass to the function.
I wrote a function "isThere" that takes 4 params max but I want to be able to call it with any number of values less than four and get the names based on only those parameter values. Let's say I want to display only the names of characters who are both tall and male. The result should be ['Igneus']
but instead I get [ 'Renna', 'Igneus', 'Igneus', 'Dagon' ]
and I couldn't figure out the logic for it to only filter the ones that met both requirements and skipped duplicates and those that met only one of them.
If I want to see the names of "sword" characters only, the result should be ["Renna" , "Igneus]
... and so on and so forth.
My code is clearly unfinished and wrong, I tried several different approaches with if statements, this is just where I stopped. If it is possible to do with an array method, that's short and clean, I'll take it. I tried Array.filter() method but I just didn't know how to implement a 4 parameter function in it, or if it'spossible at all.
const characters = [
{
bodyType: "tall",
element: "air",
gender: "female",
name: "Renna",
weapon: "sword",
},
{
bodyType: "tall",
element: "fire",
gender: "male",
name: "Igneus",
weapon: "sword",
},
{
bodyType: "medium",
element: "water",
gender: "male",
name: "Dagon",
weapon: "spear",
},
];
function isThere(body, element, gender, weapon) {
const arr = [];
for (const char of characters) {
if (char.bodyType === body) {
arr.push(char.name)
}
if(char.element === element){
arr.push(char.name)
}
if(char.gender === gender){
arr.push(char.name)
}
if(char.weapon === weapon){
arr.push(char.name)
}
}
return arr;
}
isThere("tall", undefined, "male", undefined)
CodePudding user response:
Currently your code acts as a giant "or" - it pushes the name if any of the conditions are satisfied. You're actually looking for an "and". In addition, if a criteria is not specified, the character should immediately pass the condition, which is why I have added (typeof ... !== "undefined" ? ... : ...)
.
const characters = [{
bodyType: "tall",
element: "air",
gender: "female",
name: "Renna",
weapon: "sword",
},
{
bodyType: "tall",
element: "fire",
gender: "male",
name: "Igneus",
weapon: "sword",
},
{
bodyType: "medium",
element: "water",
gender: "male",
name: "Dagon",
weapon: "spear",
},
];
function isThere(body, element, gender, weapon) {
const arr = [];
for (const char of characters) {
if ((typeof body === "undefined" ? true : char.bodyType === body) &&
(typeof element === "undefined" ? true : char.element === element) &&
(typeof gender === "undefined" ? true : char.gender === gender) &&
(typeof weapon === "undefined" ? true : char.weapon === weapon)) {
arr.push(char.name)
}
}
return arr;
}
console.log(isThere("tall", undefined, "male", undefined));
CodePudding user response:
New concept: currying
const characters = [
{
bodyType: "tall",
element: "air",
gender: "female",
name: "Renna",
weapon: "sword",
},
{
bodyType: "tall",
element: "fire",
gender: "male",
name: "Igneus",
weapon: "sword",
},
{
bodyType: "medium",
element: "water",
gender: "male",
name: "Dagon",
weapon: "spear",
},
];
const partialMatch = (object) => (character) => Object.entries(object).every(([key,value]) => character[key] === value)
const myMatchingCharacters = characters.filter(partialMatch({bodyType:'tall',gender:'male'}))
console.log(myMatchingCharacters.map(({name}) => name))
CodePudding user response:
In case the OP does not want to plainly write each filter
method like e.g. ...
-
(({ bodyType, element, gender, name, weapon }) => (bodyType === 'tall' || element === 'fire') && weapon === 'sword' )
-
(({ bodyType, element, gender, name, weapon }) => bodyType === 'tall' && gender === 'male' )
... but wants to use config like objects instead, then the OP needs to come up with helper methods like
every
andany
which each create a single filter function based on the passed config- and
and
as well asor
where each creates a single filter function which does process a list of filter functions according to the helper methods logical meaning/purpose.
function every(config) {
return (item => Object
.entries(config)
.every(([key, value]) =>
item[key] === value
)
);
}
function any(config) {
return (item => Object
.entries(config)
.some(([key, value]) =>
item[key] === value
)
);
}
function and(...requirements) {
return (item => requirements
.every(requirement =>
requirement(item)
)
);
}
function or(...requirements) {
return (item => requirements
.some(requirement =>
requirement(item)
)
);
}
const characters = [{
bodyType: "tall",
element: "air",
gender: "female",
name: "Renna",
weapon: "sword",
}, {
bodyType: "tall",
element: "fire",
gender: "male",
name: "Igneus",
weapon: "sword",
}, {
bodyType: "medium",
element: "water",
gender: "male",
name: "Dagon",
weapon: "spear",
}];
// OP ... > who is character.bodyType: "tall"
// > or has character.element: "fire"
// > AND is also character.weapon: "sword"
console.log(
'({ bodyType: "tall" } OR { element: "fire" }) AND { weapon: "sword" } ... conventionally ...',
characters
.filter(({ bodyType, element, gender, name, weapon }) =>
(bodyType === 'tall' || element === 'fire') && weapon === 'sword'
)
);
console.log(
'({ bodyType: "tall" } OR { element: "fire" }) AND { weapon: "sword" } ... by helper functions ...',
characters
.filter(
and(
any({ bodyType: "tall", element: "fire" }),
any({ weapon: "sword" })
)
)
);
// OP ... > Let's say I want to display only the names
// > of characters who are both tall and male.
console.log(
'{ bodyType: "tall" } AND { gender: "male" } ... conventionally ...',
characters
.filter(({ bodyType, element, gender, name, weapon }) =>
bodyType === 'tall' && gender === 'male'
)
);
// either ...
console.log(
'{ bodyType: "tall" } AND { gender: "male" } ... by helper functions ...',
characters
.filter(
and(
any({ bodyType: "tall" }),
any({ gender: "male" })
)
)
);
// ... or.
console.log(
'{ bodyType: "tall" } AND { gender: "male" } ... by helper functions ...',
characters
.filter(
every({ bodyType: "tall", gender: "male" }),
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
CodePudding user response:
Can be achieved using the spread syntax on the args and the array method function every().
const characters=[{bodyType:"tall",element:"air",gender:"female",name:"Renna",weapon:"sword"},{bodyType:"tall",element:"fire",gender:"male",name:"Igneus",weapon:"sword"},{bodyType:"medium",element:"water",gender:"male",name:"Dagon",weapon:"spear"}];
const isThere = (...args) =>
characters.filter((char)=>
['bodyType', 'element', 'gender', 'weapon'].every((key, index) =>
args[index] === undefined ? true : args[index] === char[key]
)
)
.map(({ name })=> name);
console.log(isThere(undefined, undefined, "male", undefined));
console.log(isThere(undefined, undefined, undefined, "sword"));