netmail-open / wjelement

advanced, flexible JSON manipulation in C
GNU Lesser General Public License v3.0
108 stars 56 forks source link

Sort an array of object literals #21

Open travispaul opened 10 years ago

travispaul commented 10 years ago

In my application I have the need to sort an array of object literals by a key, and the key can be any type. For example:

this...

{
  "myArray": [
    {"key": "B", "value": "Buzz"},
    {"key": true, "value": "Baz"},
    {"key": "42", "value": "Flam"},
    {"key": 1, "value": "Bar"},
    {"key": "A", "value": "Foo"},
    {"key": false, "value": "Flub"},
    {"key": 2, "value": "Fizz"},
  ]
}

should sort to to perhaps something like this...

{
  "myArray": [
    {"key": true, "value": "Baz"},
    {"key": false, "value": "Flub"},
    {"key": 1, "value": "Bar"},
    {"key": 2, "value": "Fizz"},
    {"key": "42", "value": "Flam"},
    {"key": "A", "value": "Foo"},
    {"key": "B", "value": "Buzz"},
  ]
}

I am not aware of any way to do this within the library itself but I can think of how to do it outside the library (looping over the array, sorting the keys and attaching the literals into a new array). I am curious if this use case is common enough that it would make sense being in the library itself, or if there was some clever way to use selectors?

minego commented 10 years ago

On 02/26/2014 02:52 PM, Travis Paul wrote:

In my application I have the need to sort an array of object literals by a key, and the key can be any type. For example:

this...

{ "myArray": [ {"key": "B", "value": "Buzz"}, {"key": true, "value": "Baz"}, {"key": "42", "value": "Flam"}, {"key": 1, "value": "Bar"}, {"key": "A", "value": "Foo"}, {"key": false, "value": "Flub"}, {"key": 2, "value": "Fizz"}, ] }

should sort to to perhaps something like this...

{ "myArray": [ {"key": true, "value": "Baz"}, {"key": false, "value": "Flub"}, {"key": 1, "value": "Bar"}, {"key": 2, "value": "Fizz"}, {"key": "42", "value": "Flam"}, {"key": "A", "value": "Foo"}, {"key": "B", "value": "Buzz"}, ] }

I am not aware of any way to do this within the library itself but I can think of how to do it outside the library (looping over the array, sorting the keys and attaching the literals into a new array). I am curious if this use case is common enough that it would make sense being in the library itself, or if there was some clever way to use selectors?

— Reply to this email directly or view it on GitHub https://github.com/netmail-open/wjelement/issues/21.

There is not a way to do this in the library currently, but I like the idea. Do you have any suggestions for what you'd like the API to look like?

Micah

penduin commented 10 years ago

We could do something like: WJESortArray(doc, compareCB) with behavior and callback conventions imitating JavaScript's Array.prototype.sort: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

It might be useful to sort objects as well as arrays, though I'm not sure if that could/should use the same mechanism or have its own WJESortObject callback would just take the key names instead of an arbitrary WJElement.

Somehow being able to use dirent's alphasort() would also be a neat trick, but I can't think of a way to do that (for arbitrary array sorting anyway) without limiting flexibility pretty severely.

travispaul commented 10 years ago

Below is what initially came to mind:

void WJEArraySortByKey(WJElement array, char *key, WJEAction order);
// {"myArray":[{"key":"A"},{"key":"B"},{"key":"C"}]}
WJEArraySortKey(WJEGet(doc, "myArray"), "key", WJE_ASCENDING);

And maybe an accompanying flat array sort?

void WJEArraySort(WJElement array, WJEAction order);
// {"myArray":["C", "B", "A"]}
WJEArraySort(WJEGet(doc, "myArray"), WJE_DESCENDING)

But that's just me thinking out loud. I like the idea of imitating JavaScript's Array.prototype.sort.

minego commented 10 years ago

On 02/26/2014 03:15 PM, penduin wrote:

We could do something like: WJESortArray(doc, compareCB) with behavior and callback conventions imitating JavaScript's Array.prototype.sort: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

It might be useful to sort objects as well as arrays, though I'm not sure if that could/should use the same mechanism or have its own WJESortObject callback would just take the key names instead of an arbitrary WJElement.

Somehow being able to use dirent's alphasort() would also be a neat trick, but I can't think of a way to do that (for arbitrary array sorting anyway) without limiting flexibility pretty severely.

— Reply to this email directly or view it on GitHub https://github.com/netmail-open/wjelement/issues/21#issuecomment-36184464.

I like this approach.

The same function would work just fine for arrays and objects.

typedef int (WJESortCB )(WJElement a, WJElement b, void data); WJESort(WJElement o, WJESortCB cb, void *data);

If the object is an array then a->name and b->name will be NULL. If it is an object then they will be non-null. If WJESort() is called on any object other than an array or an object then it will simply be a no-op.

penduin commented 10 years ago

Yeah, I like that. (Also duh, not sure why I got caught up on a separate one for objects :^) Including a couple of helpers for alphabetical sorting of strings/keys might be handy. Such a thing could even be the default, if NULL is passed for the callback. (Again emulating JS's array sort)

minego commented 10 years ago

On 02/26/2014 03:59 PM, penduin wrote:

Yeah, I like that. (Also duh, not sure why I got caught up on a separate one for objects :^) Including a couple of helpers for alphabetical sorting of strings/keys might be handy. Such a thing could even be the default, if NULL is passed for the callback. (Again emulating JS's array sort)

— Reply to this email directly or view it on GitHub https://github.com/netmail-open/wjelement/issues/21#issuecomment-36188845.

Sounds good. Do you feel like writing it? :)

travispaul commented 10 years ago

If both of you are busy with other things I would be willing to create a branch and attempt to implement it. However, I am a C novice so you will probably need to yell at me until it looks good enough to merge in. It would take me longer to implement it than either of you, but it would be a great learning experience for me and I need the feature within the next couple of months anyways.

minego commented 10 years ago

On 02/27/2014 09:57 AM, Travis Paul wrote:

If both of you are busy with other things I would be willing to create a branch and attempt to implement it. However, I am a C novice so you will probably need to yell at me until it looks good enough to merge in. It would take me longer to implement it than either of you, but it would be a great learning experience for me and I need the feature within the next couple of months anyways.

— Reply to this email directly or view it on GitHub https://github.com/netmail-open/wjelement/issues/21#issuecomment-36263945.

I would love that. I'm very happy to help with any questions about C you may have.

travispaul commented 10 years ago

Thanks @minego, I really appreciate it! I will most likely break ground next weekend (March 8th) as I have some other things to finish up this weekend.

primohan01 commented 10 years ago

Hi All, I just want to read the array elements in c language. Could you share the example of the same.

{ "Array": [1,2,3]

}

I tried through WJArray but it is returning junk values.

minego commented 10 years ago

On 10/01/14 01:08, primohan01 wrote:

Hi All, I just want to read the array elements in c language. Could you share the example of the same.

{ "Array": [1,2,3]

}

I tried through WJArray but it is returning junk values.

— Reply to this email directly or view it on GitHub https://github.com/netmail-open/wjelement/issues/21#issuecomment-57426759.

Hi,

You need to use the correct function to get the values as the C type that you want. So, in this case you would probably want to do something like this:

WJElement last; int v;

l = NULL; while (-1 != (v = _WJENumber(doc, "[]", &last, WJE_GET, -1))) { printf("Read a value: %d\n", v); }

The WJENumber() function will find the first element that matches the selector (in this case "[]" which will match any child of the outer array) attempt to convert the value to a number and return it.

The _ version is used for finding multiple matches and takes an extra argument (last) to keep track of the last match.

The final argument is the value to return if there is no match.

I hope this helps. Please don't hesitate to ask if you have any other questions.

Micah

primohan01 commented 10 years ago

Dear Micah, Could you please also suggest the Wjelement API to compare the two json object. As I need to compare the two json objects for similar values.apart form this, I believe unique property value validation in json object is not possible and there is no API available for the same.