5

I'm not too good at JS, but have survived thus far. I'm creating a sort-of complex JS object and wanting to sort it. The object's structure looks like this:

cart.attributes = [
  {
    Attribute,
    Value
  }
  ...
];

I'm creating a unique attribute that tells me 3 things separated arbitrarily by a colon:
(Product ID):(Product QTY Iterator):(Attribute's Name)
The product QTY iterator just means, if I have 3 of the same product, which of the 3 am I talking about in terms of the attribute. Each attribute has then a value.

THE PROBLEM As you'll see from the print-out, there's no organization. I'd like to sort these results first by the (Product ID), then the (QTY Iterator), then alphabetically by (Name).

Here's a print out of the object using the following method to print it out, and then the results.

CODE USING TO PRINT RESULTS

$.each(cart.attributes, function(attr, value) {
  console.log("Attr: "+attr);
  console.log("Value: "+value);
});

RESULTS

«Attr» 46913872:2:Size
«Value» 10
«Attr» 46913872:2:Hollow-to-Hem
«Value» 57"
«Attr» 46913872:1:Hips
«Value» 34"
«Attr» 46913872:2:Bust
«Value» 34"
«Attr» 46913872:2:Dress Color (hex)
«Value» #FFFFFF
«Attr» 46913872:2:Rush Cut
«Value» Super Rush Cut - 6 weeks
«Attr» 46913872:1:Extra Length
«Value» 5"
«Attr» 46913872:2:Hips
«Value» 34"
«Attr» 46913872:1:Waist
«Value» 29"
«Attr» 46913872:2:Waist
«Value» 23"
«Attr» 46913872:2:Dress Color (name)
«Value» White
«Attr» 46913872:1:Rush Cut
«Value» Super Rush Cut - 6 weeks
«Attr» 46913872:1:Sash Color (name)
«Value» Lipstick
«Attr» 46913872:2:Sash Color (hex)
«Value» #000000
«Attr» 46913872:1:Size
«Value» 14
«Attr» 46913872:1:Hollow-to-Hem
«Value» 58"
«Attr» 46913872:1:Bust
«Value» 35"
«Attr» 46913872:1:Sash Color (hex)
«Value» #B6064C
«Attr» 46913872:1:Dress Color (hex)
«Value» #F9C8D0
«Attr» 46913872:1:Dress Color (name)
«Value» Tea Rose
«Attr» 46913872:2:Extra Length
«Value» 5"
«Attr» 46913872:2:Sash Color (name)
«Value» Black
John Conde
  • 217,595
  • 99
  • 455
  • 496
Mike B.
  • 773
  • 3
  • 12
  • 26

5 Answers5

5

Ok from the comments below I got a clearer picture of what the object is.

Assuming the object looks like this:

cart.attributes = {
    '46913872:2:Size' : 10,
    '46913872:2:Hollow-to-Hem' : 57
    // ...
}

It can't be sorted since it's not an Array. But you can sort it out for printing.

In plain old javascript you can do:

// First get all keys:
var keys = [];
for (var n in cart.attributes) {
    if (cart.attributes.hasOwnProperty(n)) {
        keys.push(n);
    }
}

// now sort the keys:
keys.sort(function(a,b){
    attr_a = a.split(':');
    attr_b = b.split(':');

    // sort by product ID:
    if (parseInt(attr_a[0],10) < parseInt(attr_b[0],10)) return -1;
    if (parseInt(attr_a[0],10) > parseInt(attr_b[0],10)) return 1;
    // sort by quantity:
    if (parseInt(attr_a[1],10) < parseInt(attr_b[1],10)) return -1;
    if (parseInt(attr_a[1],10) > parseInt(attr_b[1],10)) return 1;
    // finally sort by name:
    if (attr_a[2] < attr_b[2]) return -1;
    if (attr_a[2] > attr_b[2]) return 1;
    return 0;
})

// now print out the object in key-sorted-order:
for (var i=0; i<keys.length; i++) {
    console.log("Attr: "+keys[i]);
    console.log("Value: "+cart.attributes[keys[i]]);
}
Igor
  • 33,276
  • 14
  • 79
  • 112
slebetman
  • 109,858
  • 19
  • 140
  • 171
  • Thanks @slebetman. It turns out that the object doesn't really have the items "Attribute" and "Value", those are just what I call them for explanation. I should have mentioned that :/ The object is just, cart.attrubutes = [ { '46913872:2:Size', '10' }, { '46913872:2:Hollow-to-Hem', '57"' } // ... ] Thanks. – Mike B. Jul 30 '10 at 02:05
  • That doesn't make sense, that is not a valid javascript object. Do you mean: cart.attributes = ["'46913872:2:Size','10' ", "'46913872:2:Hollow-to-Hem','57\" " // ... ] ? – slebetman Jul 30 '10 at 02:38
  • Yes. That is going to be the way it's formed slebetman. Each cart.attribute comes from an HTML element: I'm putting 3 things into the key though, so I can stuff it with more info. – Mike B. Jul 30 '10 at 02:43
  • Here's a screenshot in Firebug of the Attributes list. I just need this organized, not restructured. Thanks! Screenshot » http://grab.by/5EXw – Mike B. Jul 30 '10 at 03:12
  • Ah, ok so the cart.attributes is not an array, it is an object. You should know that things in objects are actually unordered. So there is no such thing as sorting. I'm guessing you want to sort it for display purposes? – slebetman Jul 30 '10 at 08:36
  • Thank you @slebetman and everyone else. This solution was EXACTLY what I was looking for. – Mike B. Jul 30 '10 at 16:33
3

You should be able to sort an array of objects, based on the object attributes, by writing a custom sort function like so:

function customSortByPID(a,b) {
  if (a.ProductID > b.ProductID)
  {
      return 1;
  }
  else if (a.ProductID < b.ProductID)
  {
      return -1;
  }
  return 0;
}

Where ProductID is an attribute of your object. You would call it like this:

  myArray.sort(customSortByPID);

That should at least get you started on how to write custom sort functions. Be aware that JavaScript is not strongly typed, which could lead to unexpected sorting behavior (treating an int like a string).

** DISCLAIMER ** I didn't test any of the above, it's just off the top of my head.

ThatSteveGuy
  • 1,085
  • 7
  • 11
  • 1
    This is in the right direction, explaining that you can pass a comparator. But his problem is deeper than that. He's working with a very poorly designed object... I'm typing an answer... – Ruan Mendes Jul 30 '10 at 01:35
  • Thank you both. Juan, you're right with the poorly written object. – Mike B. Jul 30 '10 at 01:43
3

First of all, it's really hard to understand the structure of the object. Is this what it looks like?

[
  {  "46913872:2:Size" : 10 },
  {  "46913872:2:Hollow-to-Hem": 57},
  {  "46913872:1:Hips" : "34"}, ...
]

It looks like you want it to sort to

[
  {  "46913872:1:Hips" : "34"}, // all ids are the same here, but this index is 1
  {  "46913872:2:Hollow-to-Hem": 57}, // index is 2 but Hollow comes before Size
  {  "46913872:2:Size" : 10 },
]

The following solution works if I understood your object structure. Like ThatSteveGuy said, you can pass a comparator to your sort method. Since the object is so complicated, this is what you would do

attributes.sort(function(a, b){
  // Make sense of your objects a and b. See the inner function defined below
  var newA = improveObject(a);
  var newB = improveObject(b);

  if (newA.id > newB.id) { return 1; }
  if (newA.id < newB.id) { return -1; }
  // id is the same, check index
  if (newA.index > newB.index) { return 1; }
  if (newA.index < newB.index) { return -1; }
  // index is the same, check name
  if (newA.name > newB.name) { return 1; }
  if (newA.name < newB.name) { return -1; }
  // it's the same thing 
  return 0;

  // Turns {  "46913872:2:Size" : 10 }
  // into {id: 46913872, index: 2, name: "Size", value: 10}
  function improveObject(obj) {
    for (var key in obj) {
      var pieces = key.split(":");
      return {
        id: parseInt(pieces[0]),
        index: parseInt(pieces[1]),
        name: pieces[2],
        value: obj[key];
      };
    }
  }
});

However, the object really doesn't make sense. I would urge you to re-think its structure. Even if I didn't fully understand your object (you should have listed the actual json for the object), the example tells you all you need to know about sorting in js.

Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • Juan, thank you so much. This is exactly what I was looking for. I'm going to take a moment to digest this and then post how I created the object from the get-go. Maybe you can help me create the object in a better manner. – Mike B. Jul 30 '10 at 02:07
  • Here's a screenshot in Firebug of the Attributes list. I just need this organized, not restructured. Thanks! Screenshot » http://grab.by/5EXw – Mike B. Jul 30 '10 at 03:13
  • I understand the object. If you want help restructuring it, you have to tell me how you're going to use this array after it's sorted. Give a thorough explanation of where this object comes from and what you are going to do with it. Not sure where is the best way to post a follow up question since you're restricted to 500 chars in comments. Maybe a new question? – Ruan Mendes Jul 30 '10 at 03:31
  • Juan, rather than me restructure what I've got, do you mind adjusting your sort() function so that it works properly with what I showed you in the screen shot? Right now it doesn't sort, and I'm 99.9% sure it has to do with improveObject(). Thanks! – Mike B. Jul 30 '10 at 03:39
  • What doesn't work? It worked for the array I showed above. I don't mind helping but we need to see some effort. Did you try debugging? Insert some console.logs and at least tell us what steps are buggy in the comparator. let us know which part is not working. Also please paste the whole array here, I can't possibly retype that whole screen. – Ruan Mendes Jul 30 '10 at 05:21
1

Something like this works well, and you can easily define a function to wrap around it and sort jQuery objects given the comparison attribute:

    var $jobj = jQuery( '.jcss' ); // your selector here
    var $jarr = [];

    $jobj.each( function () {
        $jarr.push( jQuery( this ) );
    } );

    $jarr.sort( function ( a, b ) {
        var attr = {}; // your comparison attribute here
        attr.a = parseInt( a.data( 'priority' ) || 0 );
        attr.b = parseInt( b.data( 'priority' ) || 0 );
        return attr.a < attr.b ? -1 : attr.a > attr.b ? 1 : 0;
    } );

    $jobj = jQuery( jQuery.map( $jarr, function ( obj, i ) {
        return obj[0];
    } ) );

    // whatever you need to do here
    $jobj.css( { 'text-decoration': 'blink' } );
1

You are missing the point of creating objects in the first place if you are putting multiple values in a string separated by a delimiter. The structure should instead be,

cart.attributes = [
    {
        attributes: {
            id: "46913872",
            quantityIterator: 2,
            name: "Dress Color"
        },
        value: "57",
    },
    // repeat for each product
];

Making a function to create an object from the encoded string is trivial. Split the string by ':' and record each part separately.

function makeProduct(key, val) {
    var parts = key.split(':');

    var attrs = {};
    attrs.productId = parts[0];
    attrs.quantityIterator = parts[1];
    attrs.name = parts[2];

    return { attributes: attrs, value: val};
}

Example usage:

makeProduct("46913872:2:Bust", 34); 

returns the object:

{
    attributes: {
        name: "Bust",
        productId: "46913872",
        quantityIterator: "2"
    },
    value: 34
}

A generic way to sort an object by multiple fields is listed in this answer.

Community
  • 1
  • 1
Anurag
  • 140,337
  • 36
  • 221
  • 257
  • I don't think this is the right object, why do you need an inner attributes object, I would just slap the four properties into the object, but that still doesn't make sense in my head since I have no clue what the iterator is – Ruan Mendes Jul 30 '10 at 02:07
  • Thank you all for helping. I am conforming to the rules of Shopify - an ecommerce product. I'm passing custom information through a shopping order using a bunch of HTML element . Technically, I'm cheating the system a bit and passing more than just "key", because key describes not just the name, but the id and quantity iterator. – Mike B. Jul 30 '10 at 02:26
  • QUANTITY ITERATOR: Let's say I'm buying 3 of the same shirt but on each shirt I want a different custom monogram. The 'IDs' would be the same, the 'name' would be the same, let's say "monogram" and the 'quantityIterator' would be 1, then 2, then 3. Does that clear it up Juan? – Mike B. Jul 30 '10 at 02:27
  • Here's a screenshot in Firebug of the Attributes list. I just need this organized, not restructured. Thanks! Screenshot » http://grab.by/5EXw – Mike B. Jul 30 '10 at 03:13
  • @Mike - the screenshot is very helpful. Even if the attributes in the HTML element are encoded in a string form, you can still represent each value individually in your JavaScript object, and have a `toString` function that converts it to a `"a:b:c"` form when needed. All other times, it remains in the object form. – Anurag Jul 30 '10 at 03:17
  • I send the string form to the server, server sends me back string form, then I convert it to object locally. That's fine. Do you have a function to help me convert from the string-object specified in the screenshot to an object, object like you're discussing? – Mike B. Jul 30 '10 at 03:37