How to sort a JS object of objects? - javascript

I have built an object in PHP, used JSON_encode function and send it as a JSON string to my JS script via ajax. Then I convert it back to an object. The problem I am having is that I wanted to keep the object in the order that it was originally created in. Please see this picture of what the object looks like once I get it into JS:
When I created the object, it was sorted by the customer field alphabetically. The customer name starting with A would come first, B second, etc. As you can see, now, the first element of the object as customer starting with S. It looks like somehow it got automatically sorted by the key of the top-level object, which is an integer, so I understand why this happened.
So i want to do is re-sort this object so that all the sub-objects are sorted by the customer field alphabetically. Is this possible? If so, how do I do it?
Thanks!

I've changed Fabricio Matée answer to become more flexible and return the sorted object.
function alphabetical_sort_object_of_objects(data, attr) {
var arr = [];
for (var prop in data) {
if (data.hasOwnProperty(prop)) {
var obj = {};
obj[prop] = data[prop];
obj.tempSortName = data[prop][attr].toLowerCase();
arr.push(obj);
}
}
arr.sort(function(a, b) {
var at = a.tempSortName,
bt = b.tempSortName;
return at > bt ? 1 : ( at < bt ? -1 : 0 );
});
var result = [];
for (var i=0, l=arr.length; i<l; i++) {
var obj = arr[i];
delete obj.tempSortName;
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
var id = prop;
}
}
var item = obj[id];
result.push(item);
}
return result;
}
Then just call the function like this
your_object = alphabetical_sort_object_of_objects(your_object, 'attribute_to_sort');

It's probably the difference between a JavaScript Object and a JavaScript Array. Objects are more like hash tables, where the keys aren't sorted in any particular order, whereas Arrays are linear collections of values.
In your back end, make sure you're encoding an array, rather than an object. Check the final encoded JSON, and if your collection of objects is surrounded by {} instead of [], it's being encoded as an object instead of an array.
You may run into a problem since it looks like you're trying to access the objects by an ID number, and that's the index you want those objects to occupy in the final array, which presents another problem, because you probably don't want an array with 40,000 entries when you're only storing a small amount of values.
If you just want to iterate through the objects, you should make sure you're encoding an array instead of an object. If you want to access the objects by specific ID, you'll probably have to sort the objects client-side (i.e. have the object from the JSON response, and then create another array and sort those objects into it, so you can have the sorted objects and still be able to access them by id).
You can find efficient sorting algorithms (or use the one below from ELCas) easily via Google.

Here's a generic iteration function which pushes all objects into an array and sorts them by their customer property in a case-insensitive manner, then iterates over the sorted array:
function iterate(data) {
var arr = [];
for (var prop in data) {
if (data.hasOwnProperty(prop)) {
var obj = {};
obj[prop] = data[prop];
obj.tempSortName = data[prop].customer.toLowerCase();
arr.push(obj);
}
}
arr.sort(function(a, b) {
var at = a.tempSortName,
bt = b.tempSortName;
return at > bt ? 1 : ( at < bt ? -1 : 0 );
});
for (var i = 0, l = arr.length; i < l; i++) {
var obj = arr[i];
delete obj.tempSortName;
console.log(obj);
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
var id = prop; //gets the obj "index" (id?)
}
}
console.log(id);
var item = obj[id];
console.log(item.customer);
//do stuff with item
}
}
Fiddle

sortObject(object){
if(typeof object === 'object'){
if(object instanceof Date){
return object;
}
if(object instanceof Array){
return object.map(element => this.sortObject(element));
} else {
return Object.keys(object).sort().reduce((result, key) => {
if(object[key] && object[key] !== null) {
result[key] = this.sortObject(object[key]);
}
return result;
}, {});
}
}
return object;
}

Related

storing key from hash in another array

I have a list of key-value pairs and I am trying to store the key N times in another array.
I'm struggling with the logic, if I iterate over the list_of_objects I can't see a way to keep track of which keys-values have already been assigned.
var kv={"a":2,"b":1,"c":1};
var list_of_objects=[bob,bill,jane,joe];//these are objects
GOAL
bob.kv="a"
bill.kv="a"
jane.kv="b"
joe.kv="c"
Notes in response to comment: kv is a property of the object bob (or bill, or jane, or joe)
You might want to try Object.keys()
Object.keys(kv).forEach(function(key) {
var val = kv[key];
while(val-- > 0) {
var obj = objects.shift();
obj.kv = key;
console.log(obj)
}
});
Similar to #MatUtter's answer but does not mutate the list_of_objects array:
var index = 0;
for (var key in kv) {
if (kv.hasOwnProperty(key)) {
var val = kv[key];
while (val-- > 0) {
list_of_objects[index].kv = key;
index++;
}
}
}

javascript: adding strings into an object through a class method

I'm trying to create a javascript class with a couple of methods that would let me add view and delete. I'm stuck with the add method, it should return me a unique ID of each string passed into the add method, but instead I can't figure out how to jump to a next code block when I'm done adding strings. Below is some of my current code:
var obj = {};
var arr = [];
var Words = function(){
this.add = function(newWord){
if(newWord !== false){
arr.push(newWord);
} else {
for (var i = 0; i < arr.length; i++){
obj[i] = arr[i];
return obj[i];
}
}
var words = new Words();
words.add('first');
words.add('second');
words.add('third');
I feel I should be creating a next() function of some sort that runs after I'm done pushing the last passed string, but I seem to have forgotten everything.
While I don't really know what you're trying to accomplish, there are some problems with your code...
An Object has keys and values. For every key there is a value.
Ex. mykey: "corresponding value"
for (var i = 0; i < arr.length; i++){
obj[i] = arr[i];
return obj[i];
}
You are setting obj[i] when i is an integer, so unless your keys are integers (probably not), you need to specify keys.
Otherwise, use another array for what you are doing (if possible).
If you are actually filling this object with keys that are numerals, then ignore the above.
I would also point you to using callbacks for finding out when .add() is finished (or just run your code after running .add() ??).
Why not just create an array, set it's values as you want, and retrieve them based on iteration?
var array = [];
//add
array.push("first");
//view value
console.log(array[0]); // output: "first"
//view index
array.indexOf("first"); // output 0
//delete
array.splice(0, 1);
Then wrap this as a class if you'd like:
var array = [];
var Words = function() {
this.add = function(input) {
array.push(input);
}
this.view = function(input) {
return array[input];
}
this.delete = function(input) {
array.splice(input, 1);
}
}
Hope this helps.

Get property with oldest date

I'm in a situation where I need to store some data in an object, but I can only have a set number of that data due to browser limitations. Since my app also needs to be able to get this data, I am storing it in an object where the keys of the properties are identifiers.
The data looks like this:
memory = {
13: {
last_updated: 241,
...
},
26: {
last_updated: 363,
....
}
}
last_updated would be a Date.now() string of course. This object can not have more than 6 properties. When it reaches that length, I need to start replacing the oldest properties with new data. How do I get the oldest property of the object?
One way would be to just sort the objects keys by the last updated timestamp, and pop of the last one, the oldest one
var oldest = memory[Object.keys(memory).sort(function(a,b) {
return memory[b].last_updated - memory[a].last_updated
}).pop()];
You can do something like this:
var obj = {};
for(var i = 0;i < 5; i++){
obj[i] = {date:Date.now() + i};
}
function addNew(date){
if(obj.length < 5){
obj[obj.length] = {date:date};
return;
}
var arr = [];
for(var prop in obj){
arr.push({prop:prop,date:obj[prop].date})
}
var last = arr.sort(function(a, b){return b.date-a.date})[0].prop;
obj[last].date = date;
}
addNew(Date.now() + 100);
console.log(obj);
You need to add new properties to the object only by using the addNew(...) function in order for this to work.
This would take the newest 6 items from the memory object.
var freshMemory = Object.keys(memory)
.map(function (k) { return [k, data[k]] })
.sort(function (a, b) { return b[1].last_updated - a[1].last_updated; })
.slice(0, 6)
.reduce(function (o, v) { o[v[0]] = v[1]; return o; }, {});
A short explanation
The Object.keys combined with the .map method will transfer the object to an array containing two-tuples (the key and the value)
then the .sort will sort the array by last_updated in reverse order
then the .slice will take the first 6 items
and finally .reduce will convert the array with two-tuples back to an object.

How do you search object for a property within property?

I have this object:
key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
And I have an array with only types and I need to add the given image to it, the array looks something like this:
[{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}]
Basically I want to loop the array, find the type in the key object and get the given image and save it into the array.
Is there any simple way to do this?
One thing that stands out here for me is the line
...get the given image and save it into the array
I'm assuming this means the original array. I think a better approach would be to map the appropriate keys and values to a new array but I've assumed, for this example, that it's a requirement.
In an attempt to keep the solution as terse as possible and the request for a lodash solution:
_.each(key, function(prop){
_.each(_.filter(types, { type: prop.type }), function(type) { type.image = prop.img });
});
Given the object of keys and an array of objects like so:
var key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
var arr = [{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}];
We can first create an array of the properties in the object key to make iterating it simpler.
Then loop over the array arr, and upon each member, check with a some loop which image belongs to the member by its type (some returning on the first true and ending the loop).
You can change the forEach to a map (and assign the returned new array to arr or a new variable) if you want the loop to be without side-effects, and not to mutate the original array.
var keyTypes = Object.keys(key);
arr.forEach(function (item) {
keyTypes.some(function (keyType) {
if (key[keyType].type === item.type) {
item.image = key[keyType].img;
return true;
}
return false;
});
});
The smarter thing would be to change the object of the imagetypes so that you could use the type as the accessing property, or create another object for that (as pointed out in another answer).
I'm not sure if this solution is modern, but it does not use any loops or recursion.
object = {
spawn: {type:1, img:app.assets.get('assets/spawn.svg')},
wall: {type:2, img:app.assets.get('assets/wall.svg')},
grass: {type:3, img:app.assets.get('assets/grass.svg')},
spike: {type:4, img:app.assets.get('assets/spike.svg')},
ground: {type:5, img:app.assets.get('assets/ground.svg')}
};
arr = [
{type:1, image:null},
{type:3, image:null},
{type:2, image:null},
{type:2, image:null},
{type:5, image:null}
];
var typeImages = {};
Object.getOwnPropertyNames(object).forEach(function(value){
typeImages[object[value].type] = object[value].img;
});
arr = arr.map(function(value){
return {
type: value.type,
image: typeImages[value.type]
};
});
var key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
var typesArray = [{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}];
for(var i = 0, j = typesArray.length; i < j; i++)
{
typesArray[i].image = getKeyObjectFromType(typesArray[i].type).img;
}
function getKeyObjectFromType(type)
{
for(var k in key)
{
if(key[k].type == type)
{
return key[k];
}
}
return {};
}
for (var i = 0; i < typesArray.length; i++) {
for (prop in key) {
if (key[prop].type === typesArray[i].type) {
typesArray[i].image = key[prop].img;
}
}
}
It loops through the array ("typesArray"), and for each array item, it go through all the objects in key looking for the one with the same "type". When it finds it, it takes that key object's "img" and saves into the array.
Using lodash (https://lodash.com/):
var key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
var initialList = [{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}];
var updatedList = _.transform(initialList, function(result, item) {
item.image = _.find(key, _.matchesProperty('type', item.type)).img;
result.push(item);
});
This will go over every item in the initialList, find the object that matched their type property in key and put it in the image property.
The end result will be in updatedList

Search JSON Array for a String and Retrieve Object that Contains it as a Value

My JSON is below. It contains two Objects, each with a few key value pairs. How can I search through the entire JSON array and pull the object that contains a particular string as a value?
In this case, I need to pull the object with the coupon_code: COUPON1, so that I can then pull the ID of that Coupon.
In short, I just need to get the id of the Coupon with coupon_code: COUPON1
[Object, Object]
0: Object
coupon_code: "COUPON1"
created_at: "2013-06-04T13:50:20Z"
deal_program_id: 1
id: 7
updated_at: "2013-06-04T13:50:20Z"
__proto__: Object
1: Object
coupon_code: "COUPON3"
created_at: "2013-06-04T15:47:14Z"
deal_program_id: 1
id: 8
updated_at: "2013-06-04T15:47:14Z"
Thanks :)
You just loop through the array and look. There are lots of ways to do that in JavaScript.
E.g.:
var a = /*...your array...*/;
var index = 0;
var found;
var entry;
for (index = 0; index < a.length; ++index) {
entry = a[index];
if (entry.coupon_code == "COUPON1") {
found = entry;
break;
}
}
Or using ES5's Array#some method (which is one that can be "shimmed" for browsers that don't yet have it, search for "es5 shim"):
var a = /*...your array...*/;
var found;
a.some(function(entry) {
if (entry.coupon_code == "COUPON1") {
found = entry;
return true;
}
});
Write a generic find function :
function find (arr, key, val) { // Find array element which has a key value of val
for (var ai, i = arr.length; i--;)
if ((ai = arr[i]) && ai[key] == val)
return ai;
return null;
}
Call as follows :
find (arr, 'coupon_code', 'COUPON1')
var result = null;
Objects.forEach(function(obj, i){
if(obj.cupon_code == 'COUPON1'){
return result = obj;
}
});
console.log(result);
This will loop through your Array and check the coupon_code for your specified value. If it found something, it will return it in result.
Note that Array.forEach is available since JavaScript 1.6. You might want to take a look at which browser are supporting it.

Categories

Resources