Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Steven Ventimiglia
Steven Ventimiglia
27,371 Points

Treehouse Badges API: How can I sort JS array objects (key/value pair) by value?

This has been a fun practice round for me, as a reinforcement of what I've learned so far about arrays. However, I've hit another roadblock.

On my current CodePen sandbox, I've pulled the Treehouse Badges API and displayed what was needed for the profile and badges. Then when we get to the points earned for each subject, I figured out how to sort then by value - but the main problem is that I cannot seem to pair the value with the key that it belongs to.

Here is an example of what I have so far:

    // ================
    //  POINTS SECTION
    // ================

    showPoints = "";
    myPoints = Object.values(data.points).sort(function (a,b) {return a-b}).reverse();
    console.log(myPoints);

    for (i=0; i < myPoints.length; i++) {
        showPoints += myPoints[i] + " &nbsp; ";
    }

    document.getElementById('points').innerHTML = showPoints;
    console.log(showPoints);

I have the feeling that, as I have in the past, someone will be able to throw me a clue as to what I need to do. I love a challenge, but man, this is crazy-confusing right now.

Thanks, in advance, for any help.

Source: https://codepen.io/StevenVentimiglia/pen/broMwX

What do you mean when you say that you cannot pair the value with the key?

Steven Ventimiglia
Steven Ventimiglia
27,371 Points

Im trying to sort by value of the object, but keep it paired with the key - so they both show up as:

this: 898
that: 562
them: 124
there: 52

...as opposed to:

that: 562
them: 124
there: 52
this: 898

The current issue is that they are not:

"name": "that",
"score": 562

...which seems much easier to sort. It's:

"that": 562

It's how the points are arranged within our profile .json files.

https://teamtreehouse.com/stevenventimiglia.json

2 Answers

Ah alright, well this gets into data mutation but here is a solution you could use:

// This of course would be your data.points object
const points = {
    APIs: 0,
    Android: 0,
    Business: 326,
    'C#': 1,
    CSS: 4518,
    'Data Analysis': 0,
    Databases: 0,
    Design: 2252,
    'Development Tools': 1662,
    'Digital Literacy': 219,
    'Game Development': 46,
    Go: 55,
    HTML: 1780,
    Java: 0,
    JavaScript: 3598,
    PHP: 103,
    Python: 168,
    Ruby: 531,
    Security: 0,
    'Virtual Reality': 0,
    WordPress: 1107,
    iOS: 0,
    total: 17180
}

// You were doing a sort before then reversing, in order to do less processing, just flip your
// variables :]
const values = Object.values(points).sort((a, b) => b - a) // b - a instead of a - b

function getKey(object, value) {
        // This will find the FIRST matching value
    const match = Object.keys(object).find(key => object[key] === value)

        // Warning this mutates the object so you may want to keep a clone of it for
        // reference elsewhere if needed. The reason, we delete is you will
        // always be returned the same FIRST matching value for the same number
        // but if the value is gone it can't match :]
    delete object[match]

    return match
}

for (let i = 0; i < values.length; i++) {
    console.log(`${getKey(points, values[i])}: ${values[i]}`)
}

Output:

total: 17180
CSS: 4518
JavaScript: 3598
Design: 2252
HTML: 1780
Development Tools: 1662
WordPress: 1107
Ruby: 531
Business: 326
Digital Literacy: 219
Python: 168
PHP: 103
Go: 55
Game Development: 46
C#: 1
APIs: 0
Android: 0
Data Analysis: 0
Databases: 0
Java: 0
Security: 0
Virtual Reality: 0
iOS: 0
Steven Ventimiglia
Steven Ventimiglia
27,371 Points

I'll take a longer look at this approach tonight. As mentioned above (under your first comment), this is being pulled from the Treehouse Badges API. So, I'm dealing with the data that was provided, in the format it was provided.

It also should be dynamic, so that, if any subjects are added in the future - they are placed into the page without having to make changes manually. This seems to be the hardest part. Otherwise I would have just ordered them by hand, and presto.

JSON: https://teamtreehouse.com/stevenventimiglia.json

Codepen: https://codepen.io/StevenVentimiglia/pen/broMwX

Lol. I think there might be a misunderstanding? The code I provided is an example that would need to be conformed to your page. It is dynamic and would always display the up-to-date information every time you fetched data from the server (once it is changed to consume it)

Easily changed to use dynamic code by doing this:

const values = Object.values(data.points).sort((a, b) => b - a) // now uses data.points

function getKey(object, value) {
    const match = Object.keys(object).find(key => object[key] === value)
    delete object[match]
    return match
}

for (let i = 0; i < values.length; i++) {
    console.log(`${getKey(data.points, values[i])}: ${values[i]}`) // now uses data.points
}

This code is agnostic now of anything that you give it, it doesn't care what is in the object, so if anything is added or removed it will always do the same thing. No need for changes

Input: data.points

Output: Sorted by value, highest - lowest, always

Steven Ventimiglia
Steven Ventimiglia
27,371 Points

This is a puzzle, where I've been given all the pieces, shown how to put them together - and will purposely break it down, back into pieces to understand how they fit together. There are actually a few things I'm not used to seeing in the code provided, so now I've become mildly obsessed about studying about them tonight.

Never learned the benefit of "find" in this type of situation, until now, as well as "teh crazy stuff" you threw into the console.log statement (which I'm guessing is a way of throwing a conditional in there - something I probably learned quickly and forgot.) Looked like jQuery to me at first ("$"). lol

Someone on Treehouse made me laugh in commenting to another student, "Writing code is like running a triathlon. It never gets easier, but you do get faster."

I just started a whole new race. Thanks, Joe!


Updated my pen, and will rework the code a bit tonight.

https://codepen.io/StevenVentimiglia/pen/broMwX

You know what? :] I didn't give a second thought, so I do apologize. The stuff I have written is in the latest version of JavaScript commonly referenced as ES6. If what I provided does seem foreign, then I understand the confusion, so definitely study it and feel free to ask any questions about it as I will be happy to help. Arrays are very powerful and come with so many different methods against them that can help.

Hopefully to aide in any frustrations that may arrive here is a link to the .find method

Also to provide you with some assistance in something that may be easier to understand (at least by looking at your example) I've updated the code, shown below:

// Example: given an object { a: 1, b: 2 } and a value of 1
// This method will return 'a' since the key 'a' has a value of 1
function getKey(object, value) {
    // Get the keys and return the value
    var match = Object.keys(object).find(function(key) {
        // If this is false, it will move on to the next item in the array
        // If it is true it will stop processing and return whatever
        // the value of 'key' is
        return object[key] === value
    })

    // If you are unsure about this line, comment it out and check
    // your output, it should help you understand it's purpose
    delete object[match]

    // Return the match which is the matched key associated with the given
    // 'value' parameter
    return match
}

// You have this in your example, so I am sure you understand it
var values = Object.values(data.points).sort(function (a, b) {
    // a - b returns ascending order
    // b - a returns descending order this saves extra processing and
    // honestly just does reverse
    return b - a
})

// This is equivalent to the above code (less typing)
var values Object.values(data.points).reverse()

// This is a basic loop which you implemented in your example
for (var i = 0; i < values.length; i++) {

    // This is all that funny looking stuff I had was doing, it was
    // a short-hand version
    console.log( getKey(data.points, values[i]) + ': ' + values[i] )
}

// To be even more verbose about the console.log:
for (var i = 0; i < values.length; i++) {
    var currentValueToDisplay = values[i]
    var keyToDisplay = getKey(data.points, currentValueToDisplay)

    console.log(keyToDisplay + ': ' + currentValueToDisplay)
}

I hope this additional code is not adding to any confusion :[

Steven Ventimiglia
Steven Ventimiglia
27,371 Points

Much appreciated. I now have the ability to compare and contrast between the two.

I'm actually familiar with ES6, and was able to recognize it by the use of "let". Although, they'll probably be five-years into WTF10 by the time I fully grasp it.

I love learning "vanilla" JS (and one of the reasons I purposely moved away from jQuery). Being someone who loves applying the DRY method, it's become a bit of an endless rabbit hole for me now that I'm older and realize how much I didn't know.

Between JS, eventually shifting into ES6, and Go - I'm looking forward to having a custom stack of functionality I can build upon, that will compliment my HTML, CSS and Sass skills.

0yzh 󠀠
0yzh 󠀠
17,276 Points

Hey Steven,

If I'm understanding you correctly, you are trying to pair the key to the value and print it out as such:

HTML: 123
CSS: 456
JavaScript: 789

If that is the case, then you can just use a for in loop to iterate through "data.points". Example:

// ================
//  POINTS SECTION
// ================

/* no longer needed
showPoints = "";
myPoints = Object.values(data.points).sort(function (a,b) {return a-b}).reverse();
console.log(myPoints); */

for (var p in data.points) {
    console.log(p + ' ' + data.points[p]);
}

/* This logs:
total 17180
HTML 1780
CSS 4518
Design 2252
JavaScript 3598
Ruby 531
PHP 103
WordPress 1107
iOS 0
Android 0
Development Tools 1662
Business 326
Python 168
Java 0
Digital Literacy 219
Game Development 46
C# 1
Databases 0
Virtual Reality 0
Data Analysis 0
APIs 0
Security 0
Go 55
*/

My bad if I misunderstood your question. Cheers!

Steven Ventimiglia
Steven Ventimiglia
27,371 Points

So, I took a different approach, but pretty much have the same result...

    subject = "";
    score = Object.keys(data.points).sort(function (a,b) {return a-b});
    //score = Object.values(data.points).sort(function (a,b) {return a-b}).reverse();
    console.log(score);
    for (i=0; i < score.length; i++) {
        subject += Object.keys(data.points)[i] + ": " + Object.values(data.points)[i] + " &nbsp; ";
    }
    document.getElementById('points').innerHTML = subject;

However... I would like this to be sorted from the highest value to the lowest. This seems to be the part I'm having a hard time with, since I can sort what displays if it was only the value that I needed to show - but I want to sort the key and value together, from the highest value to the lowest. The most difficult part for me is that the name of the subject is not within a key as a value, with the points scored being within another key... the subject is the key. lol

[UPDATED] Source: https://codepen.io/StevenVentimiglia/pen/broMwX

Steven Ventimiglia
Steven Ventimiglia
27,371 Points

Thanks for the effort, Huy.

Joe was more on-point with his answer above, but I appreciate you taking your time to provide a possible answer for me.