Sorting JSON by entry values - javascript
I am trying to sort through some JSON to create a list of the data where only certain rows are shown. I have some code that displays the data the way I want to display it, it just isn't sorted the way I want it. Here is the working code for the pre-sorted data:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<style type="text/css">
.items {display:table;list-style:none;margin:0;padding:0;border-spacing:5px;}
.items li {display:table-row;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;border:1px solid #ccc;padding:5px;margin:0 0 10px 0;}
.items li img {display:table-cell;vertical-align:top;width:160px;height:120px;}
.items li span.meta {display:table-cell;vertical-align:top;margin:0;padding:0 0 0 5px;font-size:6;}
.items li {margin:0 0 5px 0;}
</style>
<button onclick="mySearch()" type="button">Search</button>
<button onclick="myErase()" type="button">Clear Results</button>
<div id="home-list"></div>
<script type="text/javascript">
function mySearch() {
$('.items').remove();
//source file is https://docs.google.com/spreadsheet/ccc?key=1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2
$(function listHomes() {
$.getJSON( "https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values?alt=json-in-script&callback=?",
//this is the data that I wish to sort
function (data) {
$('div#home-list').append('<ul class="items"></ul>');
$.each(data.feed.entry, function(i,entry) {
var item = '<span style="display:none">' + entry.id.$t + '</span>';
item += '<img src="http://img.paragonrels.com/ParagonImages/Listings/P2/CGMLS/' + entry.gsx$mls.$t + '/0/960/720/4d8fbda25ff383c57b907de4c08a8677/CGMLS' + entry.gsx$mls.$t + '.JPG"/>';
item += '<span class="meta"><font size="3"><b>' + entry.title.$t + '</b></font>';
item += '<br/>City: ' + entry.gsx$city.$t;
item += '<br/>Bedrooms: ' + entry.gsx$beds.$t;
if (entry.gsx$subdivision.$t) {
item += '<br/>Description: ' + entry.gsx$subdivision.$t;
}
$('.items').append('<li>' + item + '</span></li>');
});
});
});
};
function myErase() {
$('.items').remove();
};
</script>
Here is the JSON
// API callback
JSONP({"version":"1.0","encoding":"UTF-8","feed":{"xmlns":"http://www.w3.org/2005/Atom","xmlns$openSearch":"http://a9.com/-/spec/opensearchrss/1.0/","xmlns$gsx":"http://schemas.google.com/spreadsheets/2006/extended","id":{"$t":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values"},"updated":{"$t":"2015-01-08T04:27:29.693Z"},"category":[{"scheme":"http://schemas.google.com/spreadsheets/2006","term":"http://schemas.google.com/spreadsheets/2006#list"}],"title":{"type":"text","$t":"Source Page"},"link":[{"rel":"alternate","type":"application/atom+xml","href":"https://docs.google.com/spreadsheets/d/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/pubhtml"},{"rel":"http://schemas.google.com/g/2005#feed","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values"},{"rel":"http://schemas.google.com/g/2005#post","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values"},{"rel":"self","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values?alt\u003djson-in-script"}],"author":[{"name":{"$t":"joshuam.hess"},"email":{"$t":"joshuam.hess#yourkickstart.org"}}],"openSearch$totalResults":{"$t":"4"},"openSearch$startIndex":{"$t":"1"},"entry":[{"id":{"$t":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values/cokwr"},"updated":{"$t":"2015-01-08T04:27:29.693Z"},"category":[{"scheme":"http://schemas.google.com/spreadsheets/2006","term":"http://schemas.google.com/spreadsheets/2006#list"}],"title":{"type":"text","$t":"303 Tarpon Trace \u003cbr\u003eByron, GA 31008"},"content":{"type":"text","$t":"mls: 122445, state: GA, county: Bibb County, city: Macon, subdivision: None, high: Northside, beds: 4, baths: 3, price: 250000, cars: 3"},"link":[{"rel":"self","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values/cokwr"}],"gsx$address":{"$t":"303 Tarpon Trace \u003cbr\u003eByron, GA 31008"},"gsx$mls":{"$t":"122445"},"gsx$state":{"$t":"GA"},"gsx$county":{"$t":"Bibb County"},"gsx$city":{"$t":"Macon"},"gsx$subdivision":{"$t":"None"},"gsx$high":{"$t":"Northside"},"gsx$beds":{"$t":"4"},"gsx$baths":{"$t":"3"},"gsx$price":{"$t":"250000"},"gsx$cars":{"$t":"3"}},{"id":{"$t":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values/cpzh4"},"updated":{"$t":"2015-01-08T04:27:29.693Z"},"category":[{"scheme":"http://schemas.google.com/spreadsheets/2006","term":"http://schemas.google.com/spreadsheets/2006#list"}],"title":{"type":"text","$t":"104 Gretta Court \u003cbr\u003eWarner Robins, GA 31088"},"content":{"type":"text","$t":"mls: 122444, state: GA, county: Bibb County, city: Macon, subdivision: None, high: Bibb-Westside, beds: 3, baths: 2, price: 200000, cars: 2"},"link":[{"rel":"self","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values/cpzh4"}],"gsx$address":{"$t":"104 Gretta Court \u003cbr\u003eWarner Robins, GA 31088"},"gsx$mls":{"$t":"122444"},"gsx$state":{"$t":"GA"},"gsx$county":{"$t":"Bibb County"},"gsx$city":{"$t":"Macon"},"gsx$subdivision":{"$t":"None"},"gsx$high":{"$t":"Bibb-Westside"},"gsx$beds":{"$t":"3"},"gsx$baths":{"$t":"2"},"gsx$price":{"$t":"200000"},"gsx$cars":{"$t":"2"}},{"id":{"$t":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values/cre1l"},"updated":{"$t":"2015-01-08T04:27:29.693Z"},"category":[{"scheme":"http://schemas.google.com/spreadsheets/2006","term":"http://schemas.google.com/spreadsheets/2006#list"}],"title":{"type":"text","$t":"112 Derringer Court \u003cbr\u003eByron, GA 31008"},"content":{"type":"text","$t":"mls: 120081, state: GA, county: Bibb County, city: Macon, subdivision: Woolfolk, high: See Remarks, beds: 2, baths: 2, price: 150000, cars: 1"},"link":[{"rel":"self","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values/cre1l"}],"gsx$address":{"$t":"112 Derringer Court \u003cbr\u003eByron, GA 31008"},"gsx$mls":{"$t":"120081"},"gsx$state":{"$t":"GA"},"gsx$county":{"$t":"Bibb County"},"gsx$city":{"$t":"Macon"},"gsx$subdivision":{"$t":"Woolfolk"},"gsx$high":{"$t":"See Remarks"},"gsx$beds":{"$t":"2"},"gsx$baths":{"$t":"2"},"gsx$price":{"$t":"150000"},"gsx$cars":{"$t":"1"}},{"id":{"$t":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values/chk2m"},"updated":{"$t":"2015-01-08T04:27:29.693Z"},"category":[{"scheme":"http://schemas.google.com/spreadsheets/2006","term":"http://schemas.google.com/spreadsheets/2006#list"}],"title":{"type":"text","$t":"105 Kennedy Court \u003cbr\u003eWarner Robins, GA 31093"},"content":{"type":"text","$t":"mls: 116141, state: GA, county: Macon County, city: Oglethorpe, subdivision: See Remarks, high: See Remarks, beds: 1, baths: 1, price: 50000, cars: 1"},"link":[{"rel":"self","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/1z7X-IvfauqyWul4oh_46e471nNt13ZpNhyXSIm_NXKI/ow6ewk2/public/values/chk2m"}],"gsx$address":{"$t":"105 Kennedy Court \u003cbr\u003eWarner Robins, GA 31093"},"gsx$mls":{"$t":"116141"},"gsx$state":{"$t":"GA"},"gsx$county":{"$t":"Macon County"},"gsx$city":{"$t":"Oglethorpe"},"gsx$subdivision":{"$t":"See Remarks"},"gsx$high":{"$t":"See Remarks"},"gsx$beds":{"$t":"1"},"gsx$baths":{"$t":"1"},"gsx$price":{"$t":"50000"},"gsx$cars":{"$t":"1"}}]}});
I want to sort the information by several JSON entries (beds, baths, etc.). I have tried dozens of things but have yet to get a result. I even tried sorting the data at the spreadsheet with a query, but I don't think I could get a working JSON response back.
For the sort, I'm going to use Lodash. I'll also post the same solution in pure JS for those people that want it. The first thing we need to focus on is getting a list of the keys in order. So, let's start with a test object. I'm just gonna throw in some random fields/types:
var obj= {
test : 5,
colour : 'red',
song : 'I Believe In A Thing Called Love',
profession : 'Singer',
gender : 'Male',
languages : {
array : [ 'French', 'German', 'English' ]
},
relationships : {
'sister' : 'Jasmine',
'brother' : 'Ryan'
}
}
The first thing we need to do is gain access to a list of the keys inside the object. Fortunately for us, this is pretty simple to do:
// Underscore
var keys = _.keys(object);
// → ['test', 'colour', 'song', 'profession', 'gender', 'languages', 'relationships']
// JavaScript
var keys = Object.keys(object);
// → ['test', 'colour', 'song', 'profession', 'gender', 'languages', 'relationships']
The first thing you'll notice is that this only gives us top-level keys. This is due to the fact that the inner objects are technically different objects. Were we to call either of the above on the inner object, we would get their keys too.
Ok, so we now have a list of top-level keys in the object. Naturally, the next step is to sort these keys alphabetically, which is also pretty simple. Underscore has a nice method already available for this which makes it pretty easy, but it's also very casual to implement in pure JS.
// Underscore
var sortedKeys = _.sortBy(keys, function(key){
return object[key];
});
// JavaScript
var sortedKeys = keys.sort(function(key1, key2){
var val1 = object[key1].toLowerCase();
var val2 = object[key2].toLowerCase();
if(val1 < val2 ) return -1;
if(val1 > val2 ) return 1;
return 0;
})
// Both → ['colour', 'gender', 'languages', 'profession', 'relationships', 'song', 'test']
And finally:
var sortedObj = {};
// Underscore
_.each(keys, function(key) {
sortedObj[key] = object[key];
});
// JavaScript
for(var index in keys){
var key = keys[index];
sortedObj[key] = object[key];
}
// Resulting object
{
'colour': 'red',
'gender': 'Male',
'languages': {
'array': [
'French',
'German',
'English'
]
},
'profession': 'Singer',
'relationships': {
'sister': 'Jasmine',
'brother': 'Ryan'
},
'song': 'I Believe In A Thing Called Love',
'test': 5
}
If needed for inner objects as well, we just put in a check for an object type and then recall the code if it's an object:
if(typeof object[key] == 'object'){
sortedObj[key] = sortObject(object[key]); // sortObj will be the function holding this code
} else {
sortedObj[key] = object[key];
}
Source
Related
How to weight items in a fuzzy search
Using Fuse.js, I need to weight individual item for a better ranking in search results. For instance, how do I make sure "Paris France" has the biggest score for a "Paris" query with the data below? places = [{ name: 'Paris, France' weigth: 10 },{ name: 'Paris, Ontario' weigth: 2 }, { name: 'Paris, Texas' weigth: 1 }]
As far as I am aware, there are no methods built into Fuse.js to do this. The weight property is meant to be applied to properties which are being searched (in the options object), rather than to the object that is being searched (as seen in the example here. What I might suggest is writing a function to sort this yourself. So once you get your results array back, after the search, perform an Array.sort() on it yourself (documentation here). For example... //Your places object var places = [ { name: 'Paris, Texas', weight: 2 }, { name: 'Paris, France', weight: 10 }, { name: 'Paris, Texas', weight: 1 } ]; //Your search options var options = { keys: [ "name" ] }; var fuse = new Fuse(places, options); // "list" is the item array var result = fuse.search("Paris"); //Once you have got this result, perform your own sort on it: result.sort(function(a, b) { return b.weight - a.weight; }); console.log('Your sorted results:'); console.log(result); <script src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/3.1.0/fuse.min.js"></script>
How do I get an object parameter by its id?
I need to detect an object in an array with its Id. My first array looks like that: { [id: 9, name: 'abc'], [id: 2, name 'def'], [id: 40, name: 'gh'] } (Id & name), while that other array is: { [class: 'physics', Tid: 9], [class: 'computer science', Tid: 9], [class: 'Biology', Tid: 40] }. I need to match the parameter "name" from the first array by its ID to its "class" (for example, "physics" relates to Tid=9 which is "abc" and "Biology" relates to Tid=40 which is "gh"). How can I elegantly do so without changing the way the data comes? (It comes from a database with ASP.NET web service in JSON)
You could use $http.get() which has success and error callback functions, which returns a promise object. Using this, you can setup a condition to map the id and get your desired result. Something like this. var myObject1 = {}; var myArray1 = []; var myObject2 = {}; var myArray2 = []; $http.get('json-file') .success(function(data)) { myObject1.myArray1 = data; } $http.get('json-file') .success(function(data)) { myObject2.myArray2 = data; } /* inside a loop if required */ if (myObject1.myArray1[index].id == myObject2.myArray2[index].Tid) { /* perform logic */ } This code would be present inside a service or a controller. Haven't tested it so unsure of the syntax but promise objects are the way to go. Hope this helps.
This returns an array of arrays. Each array in the array contains two objects matched by id === Tid. As far as I can tell that's what you want. (Note that I think you provided broken sample arrays, I adjusted and scrambled the numbers around so you could see it work more clearly). var arr1 = [ {id: 9, name: 'abc'}, {id: 2, name: 'def'}, {id: 40, name: 'gh'} ]; var arr2 = [ {class: 'physics', Tid: 2}, {class: 'computer science', Tid: 40}, {class: 'Biology', Tid: 9} ]; var arrFinal = arr1.map ( function ( d ) { var matched = arr2.find ( function ( obj ) { return obj.Tid === d.id; } ); return [ d, matched ]; } ); If you iterate arrFinal you'll see it contains the matched objects.
Inserting dynamic value into array of objects, javascript
I have an array of objects that look something like this; [ {Number: 5002000, Origin: 123456, Count: 128}, {Number: 5002300, Origin: 900231, Count: 52}, {Number: 5002022, Origin: 534323, Count: 269} ] Now I'm trying to multiply the "Count" value with a value from a designated price pool. Which looks something like this; [ {Prefix: 50023, Price: 20}, {Prefix: 50020, Price: 10}, {Prefix: 5002, Price: 60}, ] Currently there's an horrendous for loop with if-statements. for (var key in sData) { if (sData[key].Origin.startsWith('50023')) { sData[key].sum = (sData[key].Count * 20); } else if (sData[key].Origin.startsWith('50020')) { sData[key].sum = (sData[key].Count * 10); } // continues... } startsWith is a function that simply checks if the value starts with the (value). Is there already a function in JS to map two arrays of objects? (I'm also having issues with the logic since the "Prefix" value basically has to go from the top down as not to land on the default "5002"-prefix.)
You should use nested loops in this situation. Also switch to Array.forEach method. sData.forEach(function(item_, key) { prices.forEach(function(item) { if (sData[key].Origin.startsWith(item.Prefix)) { sData[key].sum = (sData[key].Count * item.Price); } }); })
Assuming that second array can be transformed into the hash: var tiers = { 50023: {Prefix: 50023, Price: 20}, 50020: {Prefix: 50020, Price: 10}, 5002: {Prefix: 5002, Price: 60}, } You may make it look like this: for (var key in sData) { var key = String(sData[key]) var keyIndex = key.slice(5) if (tiers.hasOwnProperty(keyIndex)) { var price = tiers[keyIndex].Price sData[key].sum = (sData[key].Count * price) } else { // Fallback solution } } Going further you may even think of some recursive solution for fallback.
(javascript) Is it possible to split an array of different object types into multiple arrays of one object type
So I have an array that contains objects with different attributes and I want to know how I can make multiple arrays with objects with the same attributes of the whole array. I want to go from this [ {name:”test”, place:”country”}, {name:”walkAndEat”, Long=100,Lat:15,Location:”place name”}, {name:”test2”,place:”Europe”} ] To [ {name:”test”, place:”country”}, {name:”test2”,place:”Europe”} ] [ {name:”walkAndEat”, Long:100,Lat:15,Location:”place name”} ]
If you see objects being equal as having the same properties, you can keep the keys as (stringified) indices in a collection object and check if a properties-key already exists: var arrcoll = {}; function add(o){ var keys = JSON.stringify(Object.keys(o).sort()); var arr = arrcoll[keys]; if(arr) arr.push(o); else arrcoll[keys] = [o]; return arr; } This can be done on the fly or on a pre existing array as shown in this Fiddle
Suppose you have a list of objects that have different properties like so: var placesOrPeople = [ { name: 'Seymour Skinner', occupation: 'Principal' }, { name: 'Kwik-E-Mart', lat: 23, long: 100 }, { name: 'Sideshow Bob', occupation: 'Comic Foil' }, { name: 'Flaming Tyre Yard', lat: 12, long: 88 }, { name: 'Joe Quimby', occupation: 'Mayor' } ]; And you want them sorted into separate lists like so: places = [ { name: 'Kwik-E-Mart', lat: 23, long: 100 }, { name: 'Flaming Tyre Yard', lat: 12, long: 88 } ]; people = [ { name: 'Seymour Skinner', occupation: 'Principal' }, { name: 'Sideshow Bob', occupation: 'Comic Foil' }, { name: 'Joe Quimby', occupation: 'Mayor' } ]; You can use the built-in Array.filter command like so: var places = placesOrPeople.filter(function(currentPlaceOrPerson) { if (currentPlaceOrPerson.occupation !== undefined) { // it must be a person, since locations don't have occupations return true; } else { return false; } }); var people = placesOrPeople.filter(function(currentPlaceOrPerson) { if (currentPlaceOrPerson.lat !== undefined && currentPlaceOrPerson.long !== undefined) { // it must be a place, since people don't have co-ordinates return true; } else { return false; } });
Javascript Objects are not set types, they are dynamic, meaning you can change them during execution. JavaScript is a loosely typed or a dynamic language. That means you don't have to declare the type of a variable ahead of time. The type will get determined automatically while the program is being processed. That also means that you can have the same variable as different types: var anything = arrayOfAnyOtherType[0]; is valid... If you loop your source array and populate it, you can define any behavior to each object
How to loop over and array with underscore
I'm trying to loop over an Array using Underscore, I have this: var friends = [ { name: 'Jimmy', age: 21 }, { name: 'Anna', age: 19 }, { name: 'Alina', age: '22' }, { name: 'Carl', age: '22' } ]; var names = _(friends).pluck('name'); for(i = 0; i < friends.length; i++){ $('ul').append('<li>' + names[i] + '</li>'); } <script src="http://underscorejs.org/underscore-min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <ul></ul> I'm trying to do the same using underscore's each method, but I can't achieve it, I'm trying doing something like this: _(friends).each(function(namessss, i){ $('ul').append('<li>' + names[i] + '</li>'); }); This actually works, but what I don't understand is why?! What's the first parameter for, it doesn't matter what I write there, it works and I wonder how to do this, and why this way works, thanks.
According to the documentation: Each invocation of iteratee is called with three arguments: (element, index, list). If list is a JavaScript object, iteratee's arguments will be (value, key, list) Your code works no matter what you write for that first argument, because you never actually use that argument (the value itself). Instead you just reference an index on the names array which you have defined outside of this loop. If you'd like to actually use the value in the each loop, you can use something like this: _(friends).each(function(friend, i){ $('ul').append('<li>' + friend.name + '</li>'); });
this would be a reasonably typical way :- var friends = [ { name: 'Jimmy', age: 21 }, { name: 'Anna', age: 19 }, { name: 'Alina', age: '22' }, { name: 'Carl', age: '22' } ]; var names = _(friends).pluck('name'); _.each(names, function(name){ $('ul').append('<li>' + name + '</li>'); }); <script src="http://underscorejs.org/underscore-min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <ul></ul>
The each method has the syntax... _.each(list, callbackFunc); where callbackFunc has the syntax... function callbackFunc(element, index, list) {} Your code just happens to work because you stored the name values in the names array which you are then accessing using the index passed to your callback function. A better way to write the code would be... _(friends).each(function(friend){ $('ul').append('<li>' + friend.name + '</li>'); });