views:

104

answers:

4

Hi,

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 3 of the same product, specifically 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, their'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

Thank you very much for your help and suggestions!

+2  A: 

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
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...
Juan Mendes
Thank you both. Juan, you're right with the poorly written object.
Mike B.
+1  A: 

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

Assuming the object looks like this:

cart.attrubutes = {
    '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]]);
}
slebetman
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.
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
Yes. That is going to be the way it's formed slebetman. Each cart.attribute comes from an HTML element: <input name="attributes[key]" value="value" /> I'm putting 3 things into the key though, so I can stuff it with more info.
Mike B.
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.
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
Thank you @slebetman and everyone else. This solution was EXACTLY what I was looking for.
Mike B.
+2  A: 

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.

Juan Mendes
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.
k, please accept my answer! :)
Juan Mendes
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.
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?
Juan Mendes
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.
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 someconsole.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.
Juan Mendes
+1  A: 

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.

Anurag
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
Juan Mendes
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 <input name="attributes['key']" value="value" />. 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.
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.
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.
@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
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.
@Mike - updated answer with such a function.
Anurag