I cant sort() Twitch.tv channels | PLEASE HELP

I cant sort() Twitch.tv channels | PLEASE HELP
0

#1

Hi campers. I’m currently working on Twitch.tv JSON API project and trying to sort() my channels alphabetically. It works fine in console but with every page reload it sorts differently. I guess it has something to do with the for loop I’m using. But I cannot figure out what exactly.

I would appreciate any hint or help! Thanks :slight_smile:

Project-link https://codepen.io/dknvmlhok/full/eyzgVm

$(document).ready(function(){
  let searchVal = $("#search").val();
  let apiUrl = "https://api.twitch.tv/kraken/channels/"+searchVal;
  let initialChannels = ["freecodecamp",
                        "ESL_SC2",
                        "OgamingSC2",
                        "cretetion",
                        "storbeck",
                        "habathcx",
                        "RobotCaleb",
                        "noobs2ninjas"];
  let sortedChannels = initialChannels.sort();
  console.log(sortedChannels);
  /*******INITIAL CHANNELS AJAX*******/
  for(let i = 0; i < sortedChannels.length; i++){
  $.ajax({
    type: 'GET',
    url: apiUrl +sortedChannels[i],
    headers: {
      'Client-ID':'aj6k9bb6snp76o59babzqeobxbbcgs'},
    success: function(data){
    $('<div id="channel-wrap">'
      +'<img src='+data.logo+' alt="Channel logo" id="logo">'
      +'<a href='+data.url+' target="_blank" id="channel-name">'+data.display_name+'</a>'
      +'</div>'+'</br>').appendTo("#card-body");
  }
  });
  };

#2

Sorting differently has nothing to do with the for loop you’re using. The sort() method is unstable and you can’t guarantee order without using a compare function.

Check the sort by name example on the link above for some help on how to sort alphabetically.

Also, the sort() method sorts in-place, which means it alters the original array. That means you can either use the same variable you have (initialChannels.sort(*)) or (better choice) you create a shallow copy using slice():

let sortedChannels = initialChannels.slice().sort(*);
* = your code inside sort

#3

You have to first sort the initialChannels array (which you did), but then to keep the order of this sorted array, you could create another array and iterate through the sorted initialChannels array and store objects containing two properties.

  1. initialChannel array index (to keep track of the original order)
  2. the created html for the specific channel

During the iteration process, once the iteration is complete, you need to sort this new array by the index property of the objects inside it and then join the html values stored in the val property of the objects. Then, you just use the join the sorted array to a string and append it to the div with id=“card-body”.

Below is the code in case you want to take a peak. You used sort() on your initialChannels array, but as @v-lai noted, the order is not what you probably wanted. My sort function in the code below sorts alphabetically regardless of the case of the channel id name.

  let initialChannels = ["freecodecamp",
                        "ESL_SC2",
                        "OgamingSC2",
                        "cretetion",
                        "storbeck",
                        "habathcx",
                        "RobotCaleb",
                        "noobs2ninjas"];

  initialChannels.sort(function(a,b) {
    return a.toLowerCase() > b.toLowerCase(); // sorts channels case-insensitive
  });
  const sortedChannels = [];
  $.each(initialChannels, function(i, channel) {
    $.ajax({
      type: "GET",
      url: apiUrl + initialChannels[i],
      headers: { "Client-ID": "aj6k9bb6snp76o59babzqeobxbbcgs" },
      success: function(data) {
        sortedChannels.push({index: i, val: '<div id="channel-wrap">' +
          "<img src=" +  data.logo +
          ' alt="Channel logo" id="logo">' +
          "<a href=" +  data.url +
          ' target="_blank" id="channel-name">' +
          data.display_name + "</a>" + "</div>" + "</br>"});
       
        if (sortedChannels.length === initialChannels.length) {
          sortedChannels.sort(function(a,b) {
            return a.index > b.index; // sorts by index
          });       
          const channelsHTML = sortedChannels.map(function(channel) {
            return channel.val;
          }).join('');

          $("#card-body").append(channelsHTML);
        }
      }
    });
  });

#4

Thanks for your reply. Unfortunately even with compare function it’s still the same.


#5

Thanks a lot for your reply! It’s more complicated than I expected and I wouldn’t be able to make this up by myself. So I leave the order the way it is. It’s not such a big deal.

BUT I ran into a something that I don’t really understand.
I’m trying to make an icon which shows online/offline channel status by color(green/red). And In that condition is also stream-info which hides when the channel is offline. The condition is correct but It’s working ONLY for the first channel. I spend a whole day trying to figure out what’s wrong but with no luck.

Have you got any idea what I can do with that please ? Thank you !

link - https://codepen.io/dknvmlhok/full/eyzgVm

for(let i = 0; i < initialChannels.length; i++){
    /*****channel name and logo access*****/
  $.ajax({
    type: 'GET',
    url: apiUrl+"channels/"+initialChannels[i],
    headers: {
      'Client-ID':'aj6k9bb6snp76o59babzqeobxbbcgs'},
    success: function(data){
      //console.log(data);
    $('<div id="channel-wrap">'
      +'<i class="fa fa-circle" aria-hidden="true" id="status-icon">'+'</i>'
      +'<img src='+data.logo+' alt="Channel logo" id="logo">'
      +'<a href='+data.url+' target="_blank" id="channel-name">'+data.display_name+'</a>'
      +'<p class="text-muted" id="stream-info">'+data.game+" : "+data.status.substr(0,30)+"..."+'</p>' 
      +'</div>'+'<br>').appendTo("#card-body");
     
      /****online/offline channel status access***/
    $.getJSON(apiUrl+"streams/"+initialChannels[i]
             +"?client_id=aj6k9bb6snp76o59babzqeobxbbcgs").done(function(data2){
      //console.log(data2);
                  if(data2.stream === null){
                      $("#status-icon").css("color","#dc3545");
                      $("#stream-info").hide();
                    }
                  else{
                      $("#status-icon").css("color","#28a745");
                      $("#stream-info").show();
                    }
                     
     });
    }
   });
  };

#6

I believe part of your problem is due to the fact that ids are meant to be unique to your html document. Each time you use the appendTo, you are putting an id=“status-icon” for i element, an id=“logo” for the img element, an id=“channel-name” for the anchor element, and an id=“stream-info” for the p element. You should be using classes not ids for this. For example, you would change the following code from:

+'<i class="fa fa-circle" aria-hidden="true" id="status-icon">'+'</i>'

to:

+'<i class="fa fa-circle status-icon" aria-hidden="true">'+'</i>'

and then update your css definitions from ids (starting with #) to classes (starting with .).

You have the same problem in:

$('<div id="channel-wrap">'

Make “channel-wrap” a class, then I assign the channel id to the id attribute like:

$('<div id="' + initialChannels[i] + '">'

Doing this above, you could replace:

if(data2.stream === null){
  $("#status-icon").css("color","#dc3545");
  $("#stream-info").hide();
}
else{
  $("#status-icon").css("color","#28a745");
  $("#stream-info").show();
}

with

if(data2.stream === null){
  $("#" + initialChannels[i] +" i").css("color","#dc3545");  // changes color of current channel's i element
  $("#" + initialChannels[i] +" p.stream-info").hide(); // hides current channel's p element with class="stream-info"
}
else{
  $("#" + initialChannels[i] +" i").css("color","#28a745");  // changes color of current channel's i element
  $("#" + initialChannels[i] +" p.stream-info").show();   // shows current channel's p element with class="stream-info"
}  

#7

Thats exactly what I needed!
Thank you very much.
It didn’t came up to my mind it could be caused by id’s. Simple. :slight_smile: