0

I am trying to sort Shopify Blog Posts by a metafield called "Event Date." I call on my articles.JSON but it does not come with the metafields.JSON so I then have to take that array and put it through a foreach function to attach the metafields to each article.

This is how the metafields.json for each article is setup:

{  
   "metafields":[  
      {  
         "id":5994805788772,
         "namespace":"global",
         "key":"Event-Date",
         "value":"1549256400",
         "value_type":"string",
         "description":null,
         "owner_id":15977611364,
         "created_at":"2019-02-06T18:31:44-05:00",
         "updated_at":"2019-02-06T18:31:44-05:00",
         "owner_resource":"article"
      },
      {  
         "id":5994805821540,
         "namespace":"global",
         "key":"Event-Time",
         "value":"6:00pm - 8:00pm",
         "value_type":"string",
         "description":null,
         "owner_id":15977611364,
         "created_at":"2019-02-06T18:31:45-05:00",
         "updated_at":"2019-02-06T18:31:45-05:00",
         "owner_resource":"article"
      },
      {  
         "id":6010564542564,
         "namespace":"global",
         "key":"Location",
         "value":"18th Street Location",
         "value_type":"string",
         "description":null,
         "owner_id":15977611364,
         "created_at":"2019-02-07T13:16:05-05:00",
         "updated_at":"2019-02-07T14:05:08-05:00",
         "owner_resource":"article"
      }
   ]
}

How I attach the metafields.JSON below:

var request = new XMLHttpRequest();

request.open('GET', '/admin/blogs/43130421348/articles.json');
request.responseType = 'json';
request.send();

request.onload = function() {
    var articleList = request.response; 
    var articleArray = articleList.articles;
    var date = new Date();
    var ticks = Math.floor(date.getTime() / 1000);
    var count = 0;
    articleArray.forEach(function(entry,index, object){
    var metaRequest = new XMLHttpRequest();
    metaRequest.open('GET', '/admin/blogs/43130421348/articles/'+ entry.id + '/metafields.json');
    metaRequest.responseType = 'json';
    metaRequest.send();
    console.log(index);

    metaRequest.onload = function() {
        var articleMetaObj = metaRequest.response;
        var articleMetaArr = articleMetaObj.metafields;
        entry.metafields = articleMetaArr; 
        var eventDate = entry.metafields[0].value;
   }

 });

};

I'm now trying to get rid of any article that has a date ("Key": "Event-Date") that has already passed compared to the current date. I've looked at the following Stack Overflow Post on removing objects in a foreach loop but none of its solutions prove to actually get rid of all the articles. It will get rid all of them occasionally but sometimes leave in one of the objects.

I've also tried an array filter but all I've gotten back is an empty array when I've used it. I've been stuck on this for a bit now so any help on solving it is much appreciated.

KC-da
  • 15
  • 6

2 Answers2

0

Is your entry the article? Then you could ignore using:

request.onload = function() {
var articleList = request.response; 
var articleArray = articleList.articles;
var date = new Date();
var ticks = Math.floor(date.getTime() / 1000);
var count = 0;
articleArray.forEach(function(entry,index, object){
   if(entry.'Key' !== 'Event-Date'){
        var metaRequest = new XMLHttpRequest();
        metaRequest.open('GET', '/admin/blogs/43130421348/articles/'+ entry.id + '/metafields.json');
        metaRequest.responseType = 'json';
        metaRequest.send();
        console.log(index);

        metaRequest.onload = function() {
        var articleMetaObj = metaRequest.response;
        var articleMetaArr = articleMetaObj.metafields;
        entry.metafields = articleMetaArr; 
        var eventDate = entry.metafields[0].value;
       }
   }
});
Raphael
  • 1,760
  • 1
  • 12
  • 21
0

I think it would be easiest if you waited until you attached all of the metadata, and then once it is all completed, use articleArray.filter to take out the ones you don't want. To do this, you have two options:

Option 1 - The Old Ways (setInterval)

Here, we keep count as the metadata is retrieved, and create an interval to check when they all have completed. Once done, a function is called (finish) that allows for continued processing.

var request = new XMLHttpRequest();

request.open('GET', '/admin/blogs/43130421348/articles.json');
request.responseType = 'json';
request.send();

request.onload = function () {
    var articleList = request.response;
    var articleArray = articleList.articles;
    var date = new Date();
    var ticks = Math.floor(date.getTime() / 1000);
    var count = 0;  //to keep track of how many metafields have been retrieved
    var checkInterval = null;

    articleArray.forEach(function (entry, index) {
        var metaRequest = new XMLHttpRequest();
        metaRequest.open('GET', '/admin/blogs/43130421348/articles/' + entry.id + '/metafields.json');
        metaRequest.responseType = 'json';
        metaRequest.send();
        console.log(index);

        metaRequest.onload = function () {
            var articleMetaObj = metaRequest.response;
            var articleMetaArr = articleMetaObj.metafields;
            entry.metafields = articleMetaArr;
            count++;
        };
    });

    //Function to continue processing
    var finish = function () {
        articleArray = articleArray.filter(a => new Date(a.metafields[0].value).getTime() < date.getTime());
        //Continue on...
    };

    //Wait until all metafields are retrieved to continue
    checkInterval = setInterval(function () {
        if (count === articleArray.length - 1) {
            clearInterval(checkInterval);
            finish();
        }
    }, 500);
};

Option 2 - The New Razmatazz (Promises & async/await)

Promises and async/await allow for writing some much nicer looking code when dealing with asynchronous operations.

If you feel like using these, I would suggest digging into the documentation to get more familiar, but here is what it could look like for your task.

//Functions that return Promises can be awaited...
var get = url => new Promise((resolve, reject) => {
    var request = new XMLHttpRequest();
    request.open('GET', url);
    request.responseType = 'json';
    //resolve is called when successful
    request.onload = () => resolve(request.response);
    //reject is called when there's a problem
    request.onerror = err => reject(err);
    request.send();
});

//await keyword must appear in an async function
var getArticles = async () => {
    var articleList = await get('/admin/blogs/43130421348/articles.json');
    return articleList.articles;
};

//Promise.all takes an array of promises and resolves when all of them are finished
//This lets you skip the messy setInterval stuff
var getArticleMetafields = async articles => {
    var requests = [];
    articles.forEach(a => {
        var url = '/admin/blogs/43130421348/articles/' + a.id + '/metafields.json';
        var promise = get(url);
        requests.push(promise);
    });
    var responses = await Promise.all(requests);
    responses.forEach((response, i) => {
        articles[i].metafields = response.metafields;
    });
    return articles;
};

//Notice the async on the handler
document.addEventListener('DOMContentLoaded', async () => {
    var articles = await getArticles();
    articles = await getArticleMetafields(articles);
    var date = new Date();
    articles = articles.filter(a => new Date(a.metafields[0].value) < date);
    //Continue...
});

Hope this helps. Cheers!

Jrd
  • 668
  • 6
  • 9
  • With waiting until I've attached everything, I would then also have to figure out how to use the json object outside of its scope? I've been trying to figure this out as well but haven't had much luck either. – KC-da Feb 12 '19 at 20:38
  • @KC-da Yeah, it would require some re-arranging; that's why I suggested the `filteredArticles` approach as well. Does that not work for you? If it doesn't, I could show you an example of how you could re-arrange it. – Jrd Feb 12 '19 at 20:58
  • The issue is that the foreach loop to add metafields to my arrays becomes cumbersome since now it's running through everything multiple times. After filtering the articles, I also want to be able to sort the remaining ones via their dates (from closest to farthest out) Would there be any way to move out of scope to break out of the foreach loop after it's done? – KC-da Feb 12 '19 at 21:18
  • @KC-da Yeah, I'll try to work up an example for ya tomorrow – Jrd Feb 12 '19 at 21:28
  • 1
    Hey my b, couldn't get to this until right now. But this is really promising and helpful. Thank you so much! – KC-da Feb 15 '19 at 19:21