Javascript sort multi-dimensional array - a complete example? - javascript

I'm no Javascript expert and I'm having problems trying to glue together the various nuggets I find here and elsewhere regarding multi-dimensional arrays and sorting and wondered if someone could help me with a complete example?
I have managed to get to the point that I can populate a localStorage with data read in via Ajax.
The format of the rows is ...
(msgXXX) (Key1:Value1|Key2:Value2|Key3:Value3|...etc)
where
(msgXXX) is the localStorage key; and
(Key1:Value1|Key2:Value2|Key3:Value3|...etc) is the single concatenated localStorage data string
What I want to be able to do is convert all this to a multi-dimensional array to which I can apply various sorts. For example, one of the Keys is called "Timestamp" and the value is an integer representing seconds since the Unix epoch. I would like to sort all rows based on this Timestamp value (in descending order - ie latest first). Right now the dataset is just over 600 rows.
I'm comfortable I can do the extraction and slicing and dicing to get the data out of the localStorage, but I'm not even sure what I'm aiming for with regards to populating an array and then setting up the sort.
Can someone point me in the right direction?

You can go with something like this:
function create(line) {
var tokens = line.split("|");
var obj = {};
for (var i = 0; i < tokens.length; i++) {
tokens[i] = tokens[i].split(":");
obj[tokens[i][0]] = tokens[i][1];
}
return obj;
}
var arr = [];
for (....) { // iterate over the input that each line is of key/value format
arr.push(create(line));
}
function timestampSort(a, b) {
if (a == b)
return 0;
return a.timestamp < b.timestamp ? -1 : 1;
}
// to sort by timestamp
arr.sort(timestampSort);
This code creates an object per key/value line, in the format you gave. The object will have the keys as attributes. All of those objects are being pushed into an array, which is then being sorted by passing a compare function to the native sort method of array.
You can of course make as many compare functions as you want, each comparing by a different attribute/criteria.
You can read more about the sort method here: http://www.w3schools.com/jsref/jsref_sort.asp
EDIT
The sort method both changes the array itself and returns the array, so doing something like:
console.log(arr.sort(timestampSort));
Will both change the actual array and return it, and so the console.log will print it.
If you don't want to change the original array and have a copy of it that will get sorted you can:
var arr2 = arr.slice();
arr2.sort(timestampSort);
As for the keys in the array, what I wrote was intended to work only with this part of the line: Key1:Value1|Key2:Value2|Key3:Value3|...etc
So, to add support for the entire format, here's the modification:
function create(line) {
var parts = line.match(/^\(msg(\d+)\) \((.+)\)$/);
var tokens = parts[2].split("|");
var obj = { msgID: parts[1] };
for (var i = 0; i < tokens.length; i++) {
tokens[i] = tokens[i].split(":");
obj[tokens[i][0]] = tokens[i][1];
}
return obj;
}
If you apply this to the example you gave you'll get this:
arr is: [{
msgID: XXX,
Key1: Value1,
Key2: Value2,
Key3: Value3
}]
Hope this clears things for you.

Related

Merge array into a single array

If this were .NET, I'd ask how to convert List<List<MyClass> to List<MyClass>. However, I'm not very good with javascript and don't know how to ask that as a question using Javascript terminology!
My javascript object comes through like
And is created as:
js_datasets.push({
"DataItem0": {
lzabel: "This",
data: [[1408710276000, null],[1408710276000, 15]]
},
"DataItem1": {
lzabel: "That",
data: [[1408710276000, null],[1408710276000, 15]]
},
});
js_datasets.push({
"DataItem22": {
lzabel: "And other",
data: [[1408710276000, null],[1408710276000, 5]]
},
"DataItem23": {
lzabel: "And lastly",
data: [[1408710276000, null],[1408710276000, 1]]
},
});
Each object is the same "type" (if it matters).
I'd like to create a single list but I am failing to do so. My efforts are
var myDataSet = []; //this is the results of what I want, ideally as a single list
for (var i = 0; i < js_datasets.length; i++) {
if (i==0) {
myDataSet.push(js_datasets[i]);
}
else {
myDataSet.concat(js_datasets[i]);//does nothing
myDataSet.join(js_datasets[i]);//does nothing
}
...more logic
As you can see with the above, I've tried using push, concat and join.
If I update the code to only use push (and never use concat and join) then I get all the values I want, but again, as an array within an array.
Using concat and join do not add to the list.
So, if we can assume the 12 items in the array (pictured) all contain 10 items, I'd like to have a single list of the 120 items!
How can I simply convert this multidimension array (is it multidimension) to a single dimension array.
This will be a bit complicated, as the items in your Array js_datasets are not Arrays, but a more generic Object. This means you can't assume the keys will be in order if you try to read them
Lets write some helper functions to account for this;
function dataItemCollectionToArray(o) {
var keys = Object.keys(o);
// assuming no non-DataItem keys, so next line commented out
// keys = keys.filter(function (e) {return e.indexOf("DataItem") === 0;});
keys.sort(function (a, b) { // ensure you will get the desired order
return +a.slice(8) - +b.slice(8);
});
return keys.map(function (e) {return o[e];});
}
Now you can loop over js_datasets performing this conversion
var myDataSet = [], i;
for (i = 0; i < js_datasets.length; ++i) {
// assuming no gaps, if you need to add gaps, also find min, max indices
// in `dataItemCollectionToArray`, and check them in each iteration here
myDataSet.push.apply(myDataSet, dataItemCollectionToArray(js_datasets[i]));
}
Please note that Object.keys and Array.prototype.map may require polifills if you wish to support old browsers, i.e. IE<=8
An easier solution however, may be to re-write how js_datasets is constructed so that the Objects you are pushing are more Array-like or indeed pushing true Arrays, perhaps with a couple extra properties so you know the offset for the first index. This would mean you can use flatten methods that you'll find around the internet

Parse data and order alphabetically under letter

This is what I would like to become:
This is my javascript:
var retrievedObject = localStorage.getItem('exhibitor');
// CALL FUNCTION
parsePerObject(JSON.parse(retrievedObject));
function parsePerObject(data){
}
This is my object in my localStorage :
{"41873":{"id":"41873","external_id":"","eventid":"5588","venueid":"0","exhibitorcategoryid":"0","name":"Niels
Vroman","shortname":"","booth":"","imageurl":"","mapid":"0","y1":"0","x1":"0","x2":"0","y2":"0","description":"Niels
uit Zulte.","tel":"0497841121","address":"Drogenboomstraat
54","email":"vroman.niels#hotmail.com","web":"http://nielsvroman.be","code":"","username":"","password":"","image1":"","imagedescription1":"","image2":"","imagedescription2":"","image3":"","imagedescription3":"","image4":"","imagedescription4":"","image5":"","imagedescription5":"","image6":"","imagedescription6":"","image7":"","imagedescription7":"","image8":"","imagedescription8":"","image9":"","imagedescription9":"","image10":"","imagedescription10":"","image11":"","imagedescription11":"","image12":"","imagedescription12":"","image13":"","imagedescription13":"","image14":"","imagedescription14":"","image15":"","imagedescription15":"","image16":"","imagedescription16":"","image17":"","imagedescription17":"","image18":"","imagedescription18":"","image19":"","imagedescription19":"","image20":"","imagedescription20":"","order":"0","brands":[],"categories":[],"linktodetails":true,"imagethumb":""},"41877":{"id":"41877","external_id":"","eventid":"5588","venueid":"0","exhibitorcategoryid":"0","name":"Ferdau
Daems","shortname":"","booth":"","imageurl":"","mapid":"0","y1":"0","x1":"0","x2":"0","y2":"0","description":"Ferdau
Daems","tel":"0497683697","address":"Waregem","email":"fer.dau#gmail.com","web":"http://ferdau.be","code":"","username":"","password":"","image1":"","imagedescription1":"","image2":"","imagedescription2":"","image3":"","imagedescription3":"","image4":"","imagedescription4":"","image5":"","imagedescription5":"","image6":"","imagedescription6":"","image7":"","imagedescription7":"","image8":"","imagedescription8":"","image9":"","imagedescription9":"","image10":"","imagedescription10":"","image11":"","imagedescription11":"","image12":"","imagedescription12":"","image13":"","imagedescription13":"","image14":"","imagedescription14":"","image15":"","imagedescription15":"","image16":"","imagedescription16":"","image17":"","imagedescription17":"","image18":"","imagedescription18":"","image19":"","imagedescription19":"","image20":"","imagedescription20":"","order":"0","brands":[],"categories":[],"linktodetails":true}}
Does anyone know how I can sort alphabetically on the name and make a header from the first letter?
Say you have an Array of objects, not an Object of objects, to enable indexing and sorting. Objects don't have order.
You retrieve it from localStorage. You parse it.
var people = JSON.parse(localStoarge.getItem("exhibitor");
// now you have an array of objects, each object representing a person.
// regardless of what structure you have now, change it to achieve this.
var comparePersons = function(a, b) {
// this function will compare two people objects.
return a.name.localeCompare(b.name);
// it's using String.prototype.localeCompare which returns 1 if a.name > b.name,
// 0 for equal and -1 for smaller. localeCompare is lexicographic comparison.
};
people.sort(comparePersons);
// now you have the people sorted alphabetically.
You can run through the people array, get the unique start letters, make an array of out them, and then display data as you want. It should be fairly simple.
var letters = '', groups = {};
for (var i = 0, len = people.length; i < len; i++) {
var letterKey = people[i].name.charAt(0).toLowerCase();// get the first letter
if (letters.indexOf(letterKey)) == -1) {
letters += letterKey;
groups[letterKey] = [people[i]];// index the people by unique letters.
} else {
groups[letterKey].push([people[i]]);// add to the existing list. Another Syntax fix
};
};
At this point you have an object like this:
a: [person1, person2, person5, etc..]//the people at A.
b: [person 3, person 4, etc..]// the people at B.
Just use the above data to create the display. Anything more and I would have to invoice you:).
The tricks here are Array.prototype.sort(here is more on it) and String.prototype.localeCompare(read more here).

Jquery fill object like array

This should be pretty easy but I'm a little confused here. I want to fill this object:
var obj = { 2:some1, 14:some2, three:some3, XX:some4, five:some5 };
but in the start I have this:
var obj = {};
I´m making a for but I don't know how to add, I was using push(), but is not working. Any help?
You can't .push() into a javascript OBJECT, since it uses custom keys instead of index. The way of doing this is pretty much like this:
var obj = {};
for (var k = 0; k<10; k++) {
obj['customkey'+k] = 'some'+k;
}
This would return:
obj {
customkey0 : 'some0',
customkey1 : 'some1',
customkey2 : 'some2',
...
}
Keep in mind, an array: ['some1','some2'] is basicly like and object:
{
0 : 'some1',
1 : 'some2'
}
Where an object replaces the "index" (0,1,etc) by a STRING key.
Hope this helps.
push() is for use in arrays, but you're creating a object.
You can add properties to an object in a few different ways:
obj.one = some1;
or
obj['one'] = some1;
I would write a simple function like this:
function pushVal(obj, value) {
var index = Object.size(obj);
//index is modified to be a string.
obj[index] = value;
}
Then in your code, when you want to add values to an object you can simply call:
for(var i=0; i<someArray.length; i++) {
pushVal(obj, someArray[i]);
}
For info on the size function I used, see here. Note, it is possible to use the index from the for loop, however, if you wanted to add multiple arrays to this one object, my method prevents conflicting indices.
EDIT
Seeing that you changed your keys in your questions example, in order to create the object, you can use the following:
function pushVal(obj, value, key) {
//index is modified to be a string.
obj[key] = value;
}
or
obj[key] = value;
I'm not sure how you determine your key value, so without that information, I can't write a solution to recreate the object, (as is, they appear random).

Sort array by key OR: Why is my for loop executing out of order?

I have an array of objects which I need placed in a certain order, depending on some configuration data. I am having a problem with itterating through the array in the proper order. I thought that if I made the array, and then stepped through with a for loop, I would be able to execute the code correctly. It is working great except in one use case, in which I add the fourth item to the array and then go back to the third.
links[0] = foo
links[1] = bar
links[2] = foobar
links[3] = a_herring
links[4] = a_shrubery
order = [] //loaded from JSON, works has the numbers 1 2 3 or 4 as values
//in this case:
order[0] = 1
order[1] = 2
order[2] = 4
order[3] = false
order[4] = 3
for(x in order){
if(order[x]){
printOrder[order[x]]=links[x]
//remember that in this case order[0] would
}
This should give me an array that looks like this:
//var printOrder[undefined,foo,bar,a_shrubbery,foobar]
But when I try to itterate through the array:
for(x in printOrder){
printOrder[x].link.appendChild(printOrder[x].image)
printOrder[x].appendChild(printOrder[x].link)
printOrder[x].appendChild(printOrder[x].text)
document.getElementById("myDiv").appendChild(printOrder[x]);
}
I get foo, bar, foobar, a_shrubbery as the output instead.
I need to either sort this array by key value, or step through it in the correct order.
Iterating over the numerically-index properties of Array instances should always be done with a numeric index:
for (var x = 0; x < printOrder.length; ++x) {
// whatever with printOrder[x]
}
Using the "for ... in" form of the statement won't get you predictable ordering, as you've seen, and it can have other weird effects too (particularly when you mix in JavaScript frameworks or tool libraries or whatever). It's used for iterating through the property names of an object, and it doesn't treat Array instances specially.
You need to create a function for finding values in an array like this:
Array.prototype.indexOf = function(value)
{
var i = this.length;
while ( i-- )
{
if ( this[ i ] == value ) return i;
}
return -1;
};
You can then use it like this:
//NOTICE: We're looping through LINKS not ORDER
for ( var i = 0; i < links.length; i++ )
{
var index = order.indexOf( i );
//It's in the order array
if ( index != -1 ) printOrder[ i ] = links[ i ];
}
REMEMBER: You need to make sure the values returned in json are integers. If they're strings, then you'll need to convert the integers to string when passed to indexOf.
The function you have in your question works as you suggest it should.
http://jsfiddle.net/NRP2D/8/ .
Clearly in this simplified case you have removed whatever error you are making in the real case.

Adding to JSON array in JavaScript/jQuery

I have data being pulled in from various sources, each returning some form of JSON or similar, although, differently formatted each time. I need to get them all into one array, but I can't figure out how to do it.
The first set is an array like this:
[
Object {id="70", type="ab", dateadded="12345678"},
Object {id="85", type="ab", dateadded="87654321"}, ... more items ...
]
The second set is being pulled in from Facebook, and is like this:
[
Object {id="12341234234", created_time="12345678"},
Object {id="567856785678", created_time="87654321"}, ... more items ...
]
So, I need to alter the second set so that it has 'type', and it has 'dateadded' instead of 'created_time', and then I need to get this all into one array so it can be sorted on 'dateadded'.
How can I do this?
Use the first array's push() method:
// for each item in second array
firstArray.push(convert(item));
function convert(obj) {
// Convert obj into format compatible with first array and return it
}
Hope this helps.
Assuming you have actual valid JSON instead of what you quoted above:
var jsonOld = '[{"id":"70","type":"ab","dateadded":"12345678"},{"id":"85","type":"ab","dateadded":"87654321"}]',
jsonNew = '[{"id":"12341234234","created_time":"12345678"},{"id":"567856785678","created_time":"87654321"}]';
Then first parse these values into actual Javascript arrays:
var mainArr = JSON.parse(jsonOld),
newArr = JSON.parse(jsonNew);
(If you already have actual Javascript arrays instead of JSON strings then skip the above step.)
Then just iterate over newArr and change the properties you need changed:
for (var i = 0, il = newArr.length; i < il; i++) {
newArr[i].type = 'ab';
newArr[i].dateadded = newArr[i].created_time;
delete newArr[i].created_time;
}
And concatenate newArr into mainArr:
mainArr = mainArr.concat(newArr);
And sort on dateadded:
mainArr.sort(function(a, b) { return a.dateadded - b.dateadded; });
This will result in:
[{"id":"70","type":"ab","dateadded":"12345678"},
{"id":"12341234234","type":"ab","dateadded":"12345678"},
{"id":"85","type":"ab","dateadded":"87654321"},
{"id":"567856785678","type":"ab","dateadded":"87654321"}]
See example

Categories