2

I have a collection initialized like this:

var invoices = new Array();
var invoice = new Invoice();
invoice.number = "123";
invoice.date = "2016-05-03";
invoice.amount = "100";
var products = new Products();

var product = new Product();
product.code = "A";
product.name = "bar";
products.push(product);   

var product2 = new Product();
product2.code = "B";
product2.name = "foo";
products.push(product2);

invoice.products = products;

Now I got to filter by the properties of the invoice like this.

var filtered = invoices.filter(function(invoice){
   return invoice.number == "123";
});

But now I want to get the invoice that matches with number and the product name

How can I do this

var filtered = invoices.filter(function(invoice){
   return invoice.number == "123" 
   // && invoice.products "name" == "foo";  //<-- At this level how can I filter?
});
0

3 Answers 3

6

Use Array.some to check for the presence of a product with the desired name:

var filtered = invoices.filter(function(invoice){
   return invoice.number == "123" && invoice.products.some(function(prod) {
       return prod.name === 'foo';
   });
});

Unlike using Array.filter and checking the length of the resulting array, this avoids creating a temporary array at all, and short-circuits; as soon as it finds a hit, it returns true immediately.

Sign up to request clarification or add additional context in comments.

1 Comment

Note: I also used safe non-type-coercing === equality checks in the some callback; ideally, you should do the same in the filter callback when checking the number. Only reason I didn't fix is that it's actually named number, and I'd worry some code might assign it a Number, and other code a string, so type-coercion might be necessary; use a consistent type and use non-coercive equality checks; coercive equality is one of the "bad parts" in JavaScript given how inconsistent/non-intuitive it can be.
1
var filtered = invoices.filter(function(invoice){
   return invoice.number == "123" && invoice.products.some(function (p) {
      return p.name == "foo";
   });
});

1 Comment

Good solution but instead of just posting code, it's a good idea to give an explanation of your answer so that the OP is sure to understand it.
0

If you want to obtain a new list of invoices with filtered products inside you need to clone invoices instead of linking them with filter:

    var filtered = []
    invoices.forEach(function (invoice) {
      if (invoice.number != "123") {
        return;
      }
      var newInvoice = new Invoice();
      newInvoice.number = invoice.number;
      newInvoice.date = invoice.date;
      newInvoice.amount = invoice.amount;
      var newProducts = invoice.products.filter(function (product) {
        return product.name == "foo"
      });
      newInvoice.products = newProducts
      filtered.push(newInvoice)
    });

UPD: as John said, if you want to filter invoices by the rule like: "Get all invoices that has number equal to 123 and has at least one product named "foo" this is the solution:

    var filtered = invoices.filter(function(invoice){
       return invoice.number == "123" && invoice.products.some(function (p) {
          return p.name == "foo";
       });
    });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.