Reputation: 1677
I'm doing an application in Angular 6 and I want to filter an array of Objects to find if one of them has the X property equal to X value.
My array of Objects:
users = [
{
"id": "3myuB3YYlHNK5m4WZC7CxrX3MvA3",
"identificationMethod": "cedula",
"identificationNumber": "23447847457",
},
{
"id": "7ruDZvmsvTVZfA59nfB7SU65gwi1",
"identificationMethod": "cedula",
"identificationNumber": "23232323232",
},
{
"id": "8IpMYYfy5dhBaR7QoQz4mXXLE1T2",
"identificationMethod": "passport",
"identificationNumber": "src34323",
}
]
I want to find all the users who has identificationMethod
equal to passport, then I want to know if there is one of these users who has identificationNumber
equal to 23232323232.
Right now, I have this snippet working:
const equalUsers: UserId[] = this.users.filter(user => user.identificationMethod === identificationMethod);
const isUnique: boolean = equalUsers.map(user => user.identificationNumber).some(value => value === identificationNumber);
(isUnique)
? console.log('Is unique')
: console.log('Is not unique');
I want to achieve the same but, combining .map()
, .filter()
and .some()
(or any other method available) in just one line.
I have tried:
const isUnique = this.users.filter(user => user.identificationMethod === identificationMethod)
.some(user => user.identificationNumber === identificationNumber);
.map()
here?.My goal is to achieve the same as the first snippet but, shorter, faster and understandable. I'm confused and I am not sure if I need to use .map()
first or is not necessary.
Upvotes: 0
Views: 4252
Reputation: 327774
I'm not sure if this is needed to be fleshed out into a full answer, but I'd suggest doing this:
this.users.some(user =>
user.identificationMethod === identificationMethod &&
user.identificationNumber === identificationNumber
);
Assuming you've got a finite-length array array
that doesn't throw errors when you read it, a one-argument function f
, and a one-argument predicate p
(a predicate is a function that returns true
or false
), the following expressions should always give you the same results:
array.map(f).some(p); // turn all x into f(x), then check that at least one p(f(x)) is true
array.some(x => p(f(x))); // check that at least one p(f(x)) is true
That is, you can compose p
and f
into a new predicate q
and just do array.some()
on that. You've already figured that one out, since in your case
f
is user => user.identificationNumber
,
p
is id => id === identificationNumber
,
so q
is user => user.identificationNumber === identificationNumber
Additionally, the following should always give you the same results:
array.filter(p).some(q); // of the x where p(x) is true, is there an x where q(x) is true?
array.some(x => p(x) && q(x)); // is there an x where both p(x) and q(x) are true?
That is, you are combining the two predicates p
and q
into a new predicate r
via conjunction. Performing that transformation on your above callbacks:
p
is user => user.identificationMethod === identificationMethod
,
q
is user => user.identificationNumber === identificationNumber
,
so r
is u => user.identificationMethod === identificationMethod && user.identificationNumber === identificationNumber
Note that although they give the same results they do not perform the same steps. The original version you have, array.filter(p).map(f).some(q)
, will end up performing p
on every single element x
of the array, then performing f
on all those that pass the test, and finally performing q
on all of those f(x)
until it finds one that passes the test. But the new version, array.some(r)
will only perform p
and f
on the elements until one passes the q
test. If the very first element of the array passes the test, the latter method will only ever look at this first element, while the former method will go through all of them.
This probably isn't a big deal in terms of performance unless your arrays are absolutely huge, and it isn't a big deal in terms of behavior unless your arrays have side effects when you read from them (like, maybe you use a Proxy
that makes changes when you read from it).
Anyway, hope that helps; good luck!
Upvotes: 1
Reputation: 9151
You can extend the condition in your Array methods.
const users = [{
"id": "3myuB3YYlHNK5m4WZC7CxrX3MvA3",
"identificationMethod": "passport",
"identificationNumber": "23447847457",
},
{
"id": "3myuB3YYlHNK5m4WZC7CxrX3MvA3",
"identificationMethod": "passport",
"identificationNumber": "23447847457",
},
{
"id": "8IpMYYfy5dhBaR7QoQz4mXXLE1T2",
"identificationMethod": "passport",
"identificationNumber": "src34323",
}
];
const identificationMethod = "passport";
const identificationNumberDuplicate = "23447847457";
const identificationNumberUnique = "src34323";
const identificationNumberDoesNotExist = "xxx";
const test = (identificationMethod, identificationNumber) => {
const found = users.filter(user => user.identificationMethod === identificationMethod &&
user.identificationNumber === identificationNumber);
const exists = found.length > 0;
const isUnique = found.length === 1;
console.log("Exists: " + exists, "Unique: " + isUnique);
};
test(identificationMethod, identificationNumberUnique);
test(identificationMethod, identificationNumberDuplicate);
test(identificationMethod, identificationNumberDoesNotExist);
Upvotes: 1