Parse data and order alphabetically under letter - javascript

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).

Related

Working with Arrays in Javascript

I'm a sudent starting to learn Javascript. We're working with arrays. The question I have is when working with two arrays, how can I match indexes when one index is chosen. Example:
var nameArray = [ "Earl", "John", "Shelia", "Lenny" ];
var testScoreArray = [ 85, 92, 87, 99 ];
If you chose Lenny, I want to be able to match his score with his name and vice versa. The arrays aren't fixed and are part of a program that can add names and scores at any time. I understand you can program it to store the names and test scores as a multi-dimensional array but not for this example.
I would like to be able to look a score up based on it being the lowest, highest or another situation and match the score with the name and vise versa.
Given this:
var array1 = ['Earl', 'John', 'Shelia', 'Lenny']
… you can get the index of "Lenny" like this:
array1.indexOf('Lenny'); //3
So you simply need to plug that into the other array:
array2[array1.indexOf('Lenny')] //99
Snippet
var array1 = ['Earl', 'John', 'Shelia', 'Lenny'],
array2 = [85,92,87,99]
console.log(array2[array1.indexOf('Lenny')]); //99
The data structure that you are looking for is a bidirectional map. Description from Wikipedia:
In computer science, a bidirectional map, or hash bag, is an associative data structure in which the (key, value) pairs form a one-to-one correspondence. Thus the binary relation is functional in each direction: value can also act as a key to key. A pair (a, b) thus provides a unique coupling between a and b so that b can be found when a is used as a key and a can be found when b is used as a key.
Mathematically, a bidirectional map between keys and values is equivalent to a pair of functions:
A function f from keys to values (i.e. a -> b).
A function g from values to keys (i.e. b -> a).
In addition, there are two constraints imposed on bidirectional maps:
Given any key x the constraint g (f x) = x must be satisfied.
Given any value y the constraint f (g y) = y must be satisfied.
Therefore, Bimap(a, b) = (a -> b, b -> a).
Alternatively, a bidirectional map can be represented as a pair of maps or associative arrays:
Since Map(a, b) = a -> b, therefore Bimap(a, b) = (Map(a, b), Map(b, a)).
Internally, an associative array could be represented as either a pair of lists (i.e. ([a], [b])) or a list of pairs (i.e. [(a, b)]) or hash tables or any other suitable data structure.
Let's implement a bidirectional map data structure using hash tables internally for efficiency:
function Bimap(keys, vals) {
var length = Math.min(keys.length, vals.length);
var valMap = this.lookupVal = Object.create(null);
var keyMap = this.lookupKey = Object.create(null);
for (var i = 0; i < length; i++) {
var key = keys[i];
var val = vals[i];
valMap[key] = val;
keyMap[val] = key;
}
}
You can use it as follows:
var bimap = new Bimap(["Earl", "John", "Shelia", "Lenny"], [85, 92, 87, 99]);
bimap.lookupVal["Lenny"]; // 99
bimap.lookupKey[99]; // "Lenny"
You can also add new key value pairs to the bidirectional map as follows:
Bimap.prototype.insert = function (keys, vals) {
var length = Math.min(keys.length, vals.length);
var valMap = this.lookupVal;
var keyMap = this.lookupKey;
for (var i = 0; i < length; i++) {
var key = keys[i];
var val = vals[i];
valMap[key] = val;
keyMap[val] = key;
}
};
You would use it as follows:
bimap.insert(["Jane"], [96]);
bimap.lookupVal["Jane"]; // 96
bimap.lookupKey[96]; // "Jane"
You can summarize bidirectional maps (such as finding min/max values, etc.) using reduce:
Bimap.prototype.reduceVals = function (inductive, basis) {
var valMap = this.lookupVal, undefined;
return basis === undefined ?
Object.keys(valMap).reduce(callback) :
Object.keys(valMap).reduce(callback, basis);
function callback(proof, key) {
return inductive(proof, valMap[key]);
}
};
For example:
bimap.lookupKey[bimap.reduceVals(Math.min)]; // "Earl"
bimap.lookupKey[bimap.reduceVals(Math.max)]; // "Lenny"
Finally, most JavaScript engines now natively support the Map data structure. Hence, you can implement Bimap in terms of that instead.
You can create object of array in javascript like this
[{Name : "Test" , Score : 25},{Name : "Test Two" , Score : 35}]
The best way to do this is by using either associative arrays or multi-dimensional arrays. I would suggest taking a look at this tutorial: www.w3schools.com/js/js_arrays.asp
If I understand your question correctly, you want to be able to match a name with a score in two different arrays. To do this, let's say they give you Lenny, first by using a for loop, you can loop through the first array, and look for his name, while keeping count on which index you're on.
The code would look like
int index = 0;
for(; index < array1.size(); index++)
When you find his name, just use that index that you found like array2[index] and you'll have your answer.

Remove value from js array without reordering keys

I have an array, and I want to remove just one element, but without reordering keys. Is there an easy way without using delete or rebuilding the entire array?
Or alternatively clean up after delete to get rid of the undefined values, fixing the length again.
var array = ["valueone", "valuetwo"];
console.dir(array); // keys 0 and 1
array.splice(0, 1);
console.dir(array); // key 1 is now 0, do not want!
You can delete the elements of an array:
a = ['one', 'two'];
delete a[0];
// a is now [undefined, 'two'];
alternatively, set a[0] explicitly to undefined.
Note that an arrays .length parameter is automatically maintained by the system. If you intentionally set it to a higher number, you'll just get a whole load of undefined values for the missing keys:
a.length = 10;
// a is now [undefined, 'two', undefined x 8]
If these semantics are not acceptable to you, then you should consider using an Object instead. This will preserve your keys, and perhaps be more efficient, but you lose the .length property.
couldn't you just explicitly set the value to undefined or null or an empty string. What are you trying to achieve?
var arr = ['aaaa','bbb','ccc','ddd'];
arr[0]= undefined;
//or
arr[0]= null;
///or
arr[0]= "";
arr.length; <--- 4
Update 2018-09-07
This answer isn't very good, in my opinion. I provided an answer on How do I remove a property from a JavaScript Object that has received much more attention from me over the years and covers this case and goes into much more detail.
The point is, you should be using Array.prototype.splice and Array.prototype.slice.
array.splice(start, n) returns a subset of array from index start with n sequential elements, and removes this subset from the original array, creating a new array in the process.
let array = [1,2,3,4,5,6];
array.splice(2,3); // [3,4,5]
array; // [1,2,6]
array.slice(start, end) returns a subset of array from index start to index end without mutating the original. The behavior is a little different from splice, which is why I prefer to call it as array.slice(start, start + n).
let array = [1,2,3,4,5,6];
array.slice(2, 2 + 3); // [3,4,5]
array; // [1,2,3,4,5,6]
Of course you could set the index to a sentinel value like null or "", but if you are wanting the array to stay in the same order after a deletion, perhaps you should change your approach--why does "valuetwo" have to be at index 1? What useful information is even being held in this data structure if the contents are always the same as the keys needed to access them?
The original answer is below. And if I am going to keep the original text, perhaps I should elaborate on why it's bad advice.
You can use javascript's delete keyword.
delete array[index];
Don't do this. If your array is homogeneous (as it ought to be), then this will corrupt your array by introducing a second type (undefined). You should use array.splice() as discussed above, which will create a new array with the specified range omitted.
Unfortunately, this creates an undefined index inside of the array
var arr = ['pie', 'cake', 'fish'];
delete arr[1];
arr; // ['pie', undefined, 'fish']
Case in point.
You could also do this:
var arr = [9,8,7,6];
arr[1] = null;
arr; // [9,null,7,6]
arr.length; // 4
var i = -1;
while(++i < arr.length){
if(arr[i] && typeof(arr[i] === "number")){
alert(arr[i]);
}
}
You could, but you shouldn't. Not only is this unnecessary, and doesn't do anything useful (because all it's doing is calling alert), but it's actually broken.
if(arr[i] && typeof(arr[i] === "number")){
alert(arr[i]);
}
You might expect this to only print our element if it is a non-zero number, but will in fact also run for values like "foo", [] and document.createElement("p"), because typeof(arr[i] === "number") will always return the value "boolean", which is a non-empty string, which is truthy and will therefore evaluate true. Which means the only requirement for alert to be called is that arr[i] is truthy. There are only six values in the entire language that will cause this if statement to not execute, and those are:
undefined
null
0
"" (pronounced "empty string")
false
NaN
Or, if you don't NEED to use arrays, you could use an object and make everything easier:
var obj = {
0: "first",
1: "second",
2: "third"
};
delete obj[1];
obj; // {0: "first", 2: "third"}
for(var i in obj){
alert(obj[i]);
}
Which would instantaneously erase all of the advantages to using an array. Now you have a data set which may or may not be heterogeneous, which can't be filtered, mapped, reduced or transformed in any sane way, and you have to resort to things like for(i in obj) (which is extremely bug-prone if you dare to use a library like jQuery) to iterate over it. Luckily today we have fancy stuff like Object.keys(obj).map(k => obj[k]).forEach(function(el){ ... }), but that's no excuse to have bad data structures.
To get the length of an object:
getLength = function(obj){
var i = 0, l = 0;
for(i in obj){
l++;
}
return l;
}
getLength(obj); // 3
Again, with arrays, this is unnecessary.
But remember that objects sort their indices by date of creation, not > by name. This shouldn't result in a road block, though.
To sort the indices of an object alphabetically:
sortObject = function (){
var arr = [], i;
for(i in this){
arr.push({index:i,content:this[i]});
delete this[i];
}
arr.sort();
for(i in arr){
var item = arr[i];
this[item.index] = item.content;
}
return this; // make chainable
}
var obj = {
acronym: "OOP",
definition: "Object-Oriented Programming",
article: "http://wikipedia.org/OOP"
};
sortObject.apply(obj); // indices are "acronym", "article", "definition"
array.sort(fn)
The whole point of an object is that its properties are unsorted, anyway. Sorting an unsorted list will hardly do anything useful.
Just to illustrate how much better arrays are at doing array things:
let array = ["pie", "cake", "fish", "brownie", "beef", ...];
/* do some stuff... */
array
.filter(el => exclude.indexOf(el) === -1)
.forEach(function(el){
console.log(el);
});
if exclude is ["cake", "brownie"], then this will log the following to the console:
pie
fish
beef
...
Just try to imagine how many unnecessary lines of code it would take to do the same using the approach from the previous version of this answer.
Hope this helped
Hopefully this update helped.

Javascript sort multi-dimensional array - a complete example?

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.

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.

Javascript sort help

I'm trying to sort some xml into different arrays and I'm a bit stuck on how to go about this.
Here is the xml: http://pastebin.ca/1754892
I'm using this xpath expression to get every "series" element that contains at least one "item" child node.
var ceerez = theXML.evaluate( '//series[item]' ,theXML, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
then I'm iterating over the result and putting the elements in a regular array:
var newArr=[];
for (var m = 0; m < ceerez.snapshotLength; m++){
newArr.push(ceerez.snapshotItem(m));
}
so what I would like to do next is to map and sort the elements in the newArr array into new arrays for each different sort.
I want to have one array that is numerically sorted by the "pub" attribute (as numbers) for each series element.
One array that is sorted alphabetically for each "title" child element's text.
One array that is sorted by date for each "item" child element.
I know how to do this with the sort() method, but I cant figure out how to do it from within the map() method so that I can make it into a new array.
var a2z1 = newArr.map(function(item){
item
.sort(function(a,b) {
return a.firstElementChild.textContent.toLowerCase().replace(/[^a-z,0-9]/gm, '') < b.firstElementChild.textContent.toLowerCase().replace(/[^a-z,0-9]/gm, '') ? -1 : 1;
});
);
(a.firstElementChild) would be the "title" element.
Make two other copies of the array (or three, if you don't want to use the original) and, using the sort method, sort each array (in place) by the criteria you specified.
Something like:
var copy1 = newArr.slice(0);
var copy2 = newArr.slice(0);
var copy3 = newArr.slice(0);
copy1.sort(function (a, b) {
// sort logic
});
copy2.sort(function (a, b) {
// sort logic
});
copy3.sort(function (a, b) {
// sort logic
});

Categories