0

I have the following array of objects:

Update: full code

export const fetchInvoices = function (firestore, currentUser) {
    const db = firestore
        .collection("customers")
        .doc(currentUser)
        .collection("subscriptions");
    let invoices = [];
    db.get().then((subscriptionSnapshot) => {
        subscriptionSnapshot.forEach((doc) => {
            doc.ref.collection("invoices").get().then((invoiceSnapshot) => {
                invoiceSnapshot.forEach(async (doc) => {
                    let invoice = {
                        "billing_reason": doc.data().billing_reason,
                        "created": doc.data().created,
                        "currency": doc.data().currency,
                        "hosted_invoice_url": doc.data().hosted_invoice_url,
                        "id": doc.id,
                        "invoice_pdf": doc.data().invoice_pdf,
                        "number": doc.data().number,
                        "period_end": doc.data().period_end,
                        "period_start": doc.data().period_start,
                        "status": doc.data().status,
                        "subtotal": doc.data().subtotal,
                        "total": doc.data().total
                    };
                    Object.defineProperty(invoice, 'invoice_id', { value: doc.id });
                    invoices.push(invoice);
                })
            })
        });
    });
    const sortedData = invoices.sort((a, b) => +(b.status === 'open') - +(a.status === 'open'));
    console.log(sortedData);
    return sortedData;
}

I want to sort them by the ones with status: "open" on the first indexes, and then sort by created. Is there a way to do this?

I've been trying to sort only the group of status: "open" using Object.values(obj).includes("open") to determine if the object has the value and then sort by truth as here. But still can't even make this "sort by group".

Jesus Jimenez
  • 351
  • 1
  • 3
  • 13
  • By group did you mean index of an array? Like objects with "status:open" in [0] and "status:created" in [1] inside the array? – Vinay Kharayat Mar 06 '21 at 14:26
  • @VinayKharayat Let's say got 4 objects with "status: open", sort them in the [0], [1], [2], [3] position respectively. – Jesus Jimenez Mar 06 '21 at 14:38
  • This looks like an async problem, have you tried logging `invoices` before sorting? I'm guessing it's empty - you're calling `invoices.sort(...` before the `db.get` resolves. see: [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – pilchard Mar 06 '21 at 15:12
  • @pilchard I do, the array has data and length of 9 – Jesus Jimenez Mar 06 '21 at 15:15

3 Answers3

1

Sorting by two different criteria is simply a matter of chaining your criteria with logical OR (||).

In the first example, first by data.status converting boolean to an integer and then by data.created .

const data = [ { "created": 1, "status": "draft", }, { "created": 5, "status": "paid", }, { "created": 2, "status": "open", }, { "created": 6, "status": "paid", }, { "created": 3, "status": "open", }, { "created": 4, "status": "paid", }, ];

const sortedData = data
  .sort((a, b) => +(b.status === 'open') - +(a.status === 'open') || a.created - b.created);

console.log(sortedData);
.as-console-wrapper { max-height: 100% !important; top: 0; }

or, since you have multiple status values, you can create a sortOrder map and reference it in your sort callback.

const data = [ { "created": 1, "status": "draft", }, { "created": 5, "status": "paid", }, { "created": 2, "status": "open", }, { "created": 6, "status": "paid", }, { "created": 3, "status": "open", }, { "created": 4, "status": "paid", }, ];

const sortOrder = {
  open: 1,
  paid: 2,
  draft: 3
}

const sortedData = data
  .sort((a, b) => sortOrder[a.status] - sortOrder[b.status] || a.created - b.created);

console.log(sortedData);
.as-console-wrapper { max-height: 100% !important; top: 0; }
pilchard
  • 12,414
  • 5
  • 11
  • 23
  • Thanks for your answer, the example you provide works with your array of objects but not with mine, is there a chance that isn't working because is not a hard-code array? I'm gonna update the full code so you can see how I'm fetching the array – Jesus Jimenez Mar 06 '21 at 15:07
  • It's working as expected with the portion of the array you provided in the question. – pilchard Mar 06 '21 at 15:11
  • please avoid chaining sort. you can just chain the deltas with logical or in a single callback. – Nina Scholz Mar 06 '21 at 17:00
0

You can get objects with open status using filter method and sort them using sort method like this.

You can change the order of sort by returning value accordingly in sort function.

const data = [
    {
        "billing_reason": "subscription_create",
        "created": 1614889156,
        "currency": "usd",
        "hosted_invoice_url": "...",
        "id": "...",
        "invoice_pdf": "...",
        "number": "82190D09-0001",
        "period_end": 1614889156,
        "period_start": 1614889156,
        "status": "draft",
        "subtotal": 3900,
        "total": 3900
    },
    {
        "billing_reason": "subscription_cycle",
        "created": 1614890009,
        "currency": "usd",
        "hosted_invoice_url": "...",
        "id": "...",
        "invoice_pdf": "...",
        "number": "82190D09-0002",
        "period_end": 1614890009,
        "period_start": 1614890009,
        "status": "paid",
        "subtotal": 3900,
        "total": 3900
    },
    {
        "billing_reason": "subscription_update",
        "created": 1614890064,
        "currency": "usd",
        "hosted_invoice_url": "...",
        "id": "...",
        "invoice_pdf": "...",
        "number": "82190D09-0003",
        "period_end": 1614890064,
        "period_start": 1614890009,
        "status": "open",
        "subtotal": -1400,
        "total": -1400
    },
    {
        "billing_reason": "subscription_create",
        "created": 1614890802,
        "currency": "usd",
        "hosted_invoice_url": "...",
        "id": "...",
        "invoice_pdf": "...",
        "number": "82190D09-0004",
        "period_end": 1614890802,
        "period_start": 1614890802,
        "status": "paid",
        "subtotal": 3900,
        "total": 3900
    },
    {
        "billing_reason": "subscription_create",
        "created": 1614892003,
        "currency": "usd",
        "hosted_invoice_url": "...",
        "id": "...",
        "invoice_pdf": "...",
        "number": "82190D09-0005",
        "period_end": 1614892002,
        "period_start": 1614892002,
        "status": "paid",
        "subtotal": 3900,
        "total": 3900
    },
    {
        "billing_reason": "subscription_create",
        "created": 1614893124,
        "currency": "usd",
        "hosted_invoice_url": "...",
        "id": "...",
        "invoice_pdf": "...",
        "number": "82190D09-0006",
        "period_end": 1614893124,
        "period_start": 1614893124,
        "status": "paid",
        "subtotal": 3900,
        "total": 3900
    },
    
]

const open = data.filter(d => d.status === "open").sort((a,b) => a.created - b.created);

console.log(open);
timbersaw
  • 620
  • 5
  • 9
0

If you're open to using some great libraries out there this solution might work for you using underscore which is great for situations like this.

Assuming you put your original array into a variable called data as in Solvenc1no's answer :

    const _ = require('underscore');
    const sortedAndGrouped = _.groupBy(_.sortBy(data, "created"),'status');
    console.log(sortedAndGrouped);

Which results in this output:

{ draft:
   [ { billing_reason: 'subscription_create',
       created: 1614889156,
       currency: 'usd',
       hosted_invoice_url: '...',
       id: '...',
       invoice_pdf: '...',
       number: '82190D09-0001',
       period_end: 1614889156,
       period_start: 1614889156,
       status: 'draft',
       subtotal: 3900,
       total: 3900 } ],
  paid:
   [ { billing_reason: 'subscription_cycle',
       created: 1614890009,
       currency: 'usd',
       hosted_invoice_url: '...',
       id: '...',
       invoice_pdf: '...',
       number: '82190D09-0002',
       period_end: 1614890009,
       period_start: 1614890009,
       status: 'paid',
       subtotal: 3900,
       total: 3900 },
     { billing_reason: 'subscription_create',
       created: 1614890802,
       currency: 'usd',
       hosted_invoice_url: '...',
       id: '...',
       invoice_pdf: '...',
       number: '82190D09-0004',
       period_end: 1614890802,
       period_start: 1614890802,
       status: 'paid',
       subtotal: 3900,
       total: 3900 },
     { billing_reason: 'subscription_create',
       created: 1614892003,
       currency: 'usd',
       hosted_invoice_url: '...',
       id: '...',
       invoice_pdf: '...',
       number: '82190D09-0005',
       period_end: 1614892002,
       period_start: 1614892002,
       status: 'paid',
       subtotal: 3900,
       total: 3900 },
     { billing_reason: 'subscription_create',
       created: 1614893124,
       currency: 'usd',
       hosted_invoice_url: '...',
       id: '...',
       invoice_pdf: '...',
       number: '82190D09-0006',
       period_end: 1614893124,
       period_start: 1614893124,
       status: 'paid',
       subtotal: 3900,
       total: 3900 } ],
  open:
   [ { billing_reason: 'subscription_update',
       created: 1614890064,
       currency: 'usd',
       hosted_invoice_url: '...',
       id: '...',
       invoice_pdf: '...',
       number: '82190D09-0003',
       period_end: 1614890064,
       period_start: 1614890009,
       status: 'open',
       subtotal: -1400,
       total: -1400 } ] }
darrin
  • 749
  • 5
  • 23
  • It's not quite the result I'm looking for, as you can see in the array i'm providing the first index [0] has status "draft". followed by status "paid" then "open". I want objects with status "open" to be in the first indexes 0, 1, 2... if there are more than one as it could be a possibility. – Jesus Jimenez Mar 06 '21 at 14:57