I am trying to build a custom sort function for a Weight Loss Challenge application and when users tries to click on Week 1 to sort the below JSON format data, the result doesn't appear to be correct. Appreciate if you can guide with what I am missing here. I would be changing the week 1 to another week if the function works out.
var array=[
{
"user": "Noah",
"weeklyPercentageChange":["10.37","-3.75","-2.21","-1.85","-1.68","-1.21","-1.41","-1.17","-0.47","-1.64","-23.36" ]
},
{
"user": "Liam",
"weeklyPercentageChange":["-4.49","-2.61","-1.19","-0.36","-1.39","-1.29","-0.44","-0.22","-0.67","-0.33","-12.53" ]
},
{
"user": "Mason",
"weeklyPercentageChange":["3.22","-2.40","0.35","-1.80","-0.25","-0.66","-2.42","0.00","1.71","-0.52","-9.00" ]
},
{
"user": "Ethan",
"weeklyPercentageChange":["-1.31","0.00","-1.75","-0.78","0.61","-1.79","0.80","0.00","0.00","0.38","-3.75"]
},
{
"user": "Alexander",
"weeklyPercentageChange":["-1.26","0.00","-1.48","-1.50","-0.07","-1.66","-0.53","-0.20","0.00","-0.86","-7.42"]
},
{
"user": "Maria",
"weeklyPercentageChange":["-1.10","-1.90","-0.11","-0.63","2.52","-1.01","0.06","-0.06","-0.06","1.38","-0.94" ]
},
{
"user": "Paul",
"weeklyPercentageChange":["1.08","-2.33","-0.10","-1.17","1.83","-1.70","-1.13","-0.19","0.00","0.00","-5.84"]
},
{
"user": "Steven",
"weeklyPercentageChange":["-0.77","-2.93","0.19","-1.22","-0.48","-1.39","-1.27","0.00","-0.23","-0.91","-8.84" ]
},
{
"user": "Edward",
"weeklyPercentageChange":["-0.71","-1.73","1.46","-0.17","0.42","-1.23","-1.26","1.47","0.59","-2.81","-3.98"]
},
{
"user": "Benjamin",
"weeklyPercentageChange":["0.56","-0.28","0.80","-0.94","0.00","0.90","-0.05","1.17","-1.87","0.98","0.14"]
},
{
"user": "Zachary",
"weeklyPercentageChange":["-0.47","-1.29","0.48","-1.59","1.37","-2.65","-0.24","0.66","0.05","0.24","-3.41"]
},
{
"user": "Austin",
"weeklyPercentageChange":["-0.33","0.00","-3.05","-2.02","0.80","-1.48","1.58","-0.43","0.87","-1.90","-5.87"]
},
{
"user": "Jordan",
"weeklyPercentageChange":["0.06","-2.18","2.17","-0.56","0.84","-0.67","0.45","0.39","0.28","0.06","0.67"]
},
{
"user": "Gabriel",
"weeklyPercentageChange":["0.00","0.00","-0.12","-0.81","0.25","0.00","0.00","0.00","0.00","0.00","-0.68"]
},
{
"user": "Wayne",
"weeklyPercentageChange":["0.20","-3.45","0.35","0.49","0.56","0.00","-0.61","0.54","0.54","-0.41","-1.83"]
},
{
"user": "Willie",
"weeklyPercentageChange":["-0.83","-3.21","0.71","0.00","-1.05","0.35","0.07","0.14","-0.07","-1.45","-3.72"]
},
{
"user": "Frank",
"weeklyPercentageChange":["0.00","0.00","-3.04","0.88","1.37","-0.41","-1.21","2.10","0.00","-0.58","-0.94"]
}
];
function GetSortOrder(prop){
return function(a,b){
if( a[prop] > b[prop]){
return 1;
}else if( a[prop] < b[prop] ){
return -1;
}
return 0;
}
}
/* array.sort( GetSortOrder("user") );
document.write("Sorted User Names : <br>");
for (var item in array) {
document.write("<br>"+array[item].user);
} */
array.sort( GetSortOrder("weeklyPercentageChange[0]") );
document.write("<br><br> Sorted Weekly Percentage Change : <br>");
for (var item in array) {
document.write("<br>"+array[item].weeklyPercentageChange[0]);
}
GetSortOrder("weeklyPercentageChange[0]") will result in the literal property name "weeklyPercentageChange[0]" being looked up - that is, if the objects were defined something like
{
user: 'name',
'weeklyPercentageChange[0]': 55
}
This isn't what you have, which is why things aren't working as expected.
For a general solution of navigating a nested object or array to find a value to sort by, consider passing a callback instead, which navigates to the nested value you want to compare against; here, obj => obj.weeklyPercentageChange[0].
You can also make the code significantly more concise by simply returning the difference between the values:
var array=[{user:"Noah",weeklyPercentageChange:["10.37","-3.75","-2.21","-1.85","-1.68","-1.21","-1.41","-1.17","-0.47","-1.64","-23.36"]},{user:"Liam",weeklyPercentageChange:["-4.49","-2.61","-1.19","-0.36","-1.39","-1.29","-0.44","-0.22","-0.67","-0.33","-12.53"]},{user:"Mason",weeklyPercentageChange:["3.22","-2.40","0.35","-1.80","-0.25","-0.66","-2.42","0.00","1.71","-0.52","-9.00"]},{user:"Ethan",weeklyPercentageChange:["-1.31","0.00","-1.75","-0.78","0.61","-1.79","0.80","0.00","0.00","0.38","-3.75"]},{user:"Alexander",weeklyPercentageChange:["-1.26","0.00","-1.48","-1.50","-0.07","-1.66","-0.53","-0.20","0.00","-0.86","-7.42"]},{user:"Maria",weeklyPercentageChange:["-1.10","-1.90","-0.11","-0.63","2.52","-1.01","0.06","-0.06","-0.06","1.38","-0.94"]},{user:"Paul",weeklyPercentageChange:["1.08","-2.33","-0.10","-1.17","1.83","-1.70","-1.13","-0.19","0.00","0.00","-5.84"]},{user:"Steven",weeklyPercentageChange:["-0.77","-2.93","0.19","-1.22","-0.48","-1.39","-1.27","0.00","-0.23","-0.91","-8.84"]},{user:"Edward",weeklyPercentageChange:["-0.71","-1.73","1.46","-0.17","0.42","-1.23","-1.26","1.47","0.59","-2.81","-3.98"]},{user:"Benjamin",weeklyPercentageChange:["0.56","-0.28","0.80","-0.94","0.00","0.90","-0.05","1.17","-1.87","0.98","0.14"]},{user:"Zachary",weeklyPercentageChange:["-0.47","-1.29","0.48","-1.59","1.37","-2.65","-0.24","0.66","0.05","0.24","-3.41"]},{user:"Austin",weeklyPercentageChange:["-0.33","0.00","-3.05","-2.02","0.80","-1.48","1.58","-0.43","0.87","-1.90","-5.87"]},{user:"Jordan",weeklyPercentageChange:["0.06","-2.18","2.17","-0.56","0.84","-0.67","0.45","0.39","0.28","0.06","0.67"]},{user:"Gabriel",weeklyPercentageChange:["0.00","0.00","-0.12","-0.81","0.25","0.00","0.00","0.00","0.00","0.00","-0.68"]},{user:"Wayne",weeklyPercentageChange:["0.20","-3.45","0.35","0.49","0.56","0.00","-0.61","0.54","0.54","-0.41","-1.83"]},{user:"Willie",weeklyPercentageChange:["-0.83","-3.21","0.71","0.00","-1.05","0.35","0.07","0.14","-0.07","-1.45","-3.72"]},{user:"Frank",weeklyPercentageChange:["0.00","0.00","-3.04","0.88","1.37","-0.41","-1.21","2.10","0.00","-0.58","-0.94"]}];
const GetSortOrder = getProp => (a,b) => getProp(a) - getProp(b);
array.sort( GetSortOrder(obj => obj.weeklyPercentageChange[0]) );
document.write("<br><br> Sorted Weekly Percentage Change : <br>");
for (var item in array) {
document.write("<br>"+array[item].weeklyPercentageChange[0]);
}
This is my saved localstorage,
[{"industry_Id":1,"merchant_id":2}]
I want to filter below result, to get HP.
{
"industries": [
{
"id": 1,
"name": "oil and gas",
"merchant": [
{
"id": 1,
"name": "ABC",
},
{
"id": 2,
"name": "DEF",
},
{
"id": 3,
"name": "GHJ",
}
]
},
{
"id": 2,
"name": "IT",
"merchant": [
{
"id": 1,
"name": "Apple",
},
{
"id": 2,
"name": "HP",
},
{
"id": 3,
"name": "Google",
}
]
}
]
}
I thought of using multiple $.each but it have to iterate few times and it's quite redundant.
I would prefer using Javascript for loop, that way you can skip iterating over every object once required element is found.
Without jQuery (using for)
var i, j, merchant = null;
for(i = 0; i < data['industries'].length; i++){
if(data['industries'][i]['id'] == arg[0]['industry_Id']){
for(j = 0; j < data['industries'][i]['merchant'].length; j++){
if(data['industries'][i]['merchant'][j]['id'] == arg[0]['merchant_id']){
merchant = data['industries'][i]['merchant'][j];
break;
}
}
if(merchant !== null){ break; }
}
}
With jQuery (using $.each)
var merchant_found = null;
$.each(data['industries'], function(i, industry){
if(industry['id'] == arg[0]['industry_Id']){
$.each(industry['merchant'], function(i, merchant){
if(merchant['id'] == arg[0]['merchant_id']){
merchant_found = merchant;
}
return (!merchant_found);
});
}
return (!merchant_found);
});
var arg = [{"industry_Id":1,"merchant_id":2}];
var data = {
"industries": [
{
"id": 1,
"name": "oil and gas",
"merchant": [
{
"id": 1,
"name": "ABC",
},
{
"id": 2,
"name": "DEF",
},
{
"id": 3,
"name": "GHJ",
}
]
},
{
"id": 2,
"name": "IT",
"merchant": [
{
"id": 1,
"name": "Apple",
},
{
"id": 2,
"name": "HP",
},
{
"id": 3,
"name": "Google",
}
]
}
]
};
var i, j, merchant = null;
for(i = 0; i < data['industries'].length; i++){
if(data['industries'][i]['id'] == arg[0]['industry_Id']){
for(j = 0; j < data['industries'][i]['merchant'].length; j++){
if(data['industries'][i]['merchant'][j]['id'] == arg[0]['merchant_id']){
merchant = data['industries'][i]['merchant'][j];
break;
}
}
if(merchant !== null){ break; }
}
}
console.log(merchant);
document.writeln("<b>Without jQuery:</b><br>");
document.writeln((merchant !== null) ? "Found " + merchant['name'] : "Not found");
var merchant_found = null;
$.each(data['industries'], function(i, industry){
if(industry['id'] == arg[0]['industry_Id']){
$.each(industry['merchant'], function(i, merchant){
if(merchant['id'] == arg[0]['merchant_id']){
merchant_found = merchant;
}
return (!merchant_found);
});
}
return (!merchant_found);
});
console.log(merchant_found);
document.writeln("<br><br><b>With jQuery:</b><br>");
document.writeln((merchant_found) ? "Found " + merchant_found['name'] : "Not found");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
selectors.map(function(selector) {
return data.industries.filter(function(industry) {
return industry.id == selector.industry_Id;
})[0].merchant.filter(function(merchant) {
return merchant.id == selector.merchant_id;
})[0].name;
});
// => DEF
If you want "HP", you want industry 2, not industry 1.
.filter(...)[0] is not really optimal. You could use .find(...), but that is not yet universally supported. Or you could use plain old JavaScript and write for loops instead to make it fast. Or you could use objects with ID keys instead of arrays to make lookups faster.
When it comes into a position where collection of data is what you're processing, I suggest you to take a look at underscore.js. It's not optimal choice for the best performance but it does make you code more readable and makes more sense especially when compared with loop.
Say data is a variable which stores your JSON data.
Try this:
// Given this selector criteria
var select = [{"industry_Id":1,"merchant_id":2}];
function filterByCriteria(criteria, data){
var match = [];
_.each(criteria, function(crit){
function matchIndustry(rec){ return rec.id===crit.industry_Id }
function matchMerchant(rec){ return rec.id===crit.merchant_id }
// Filter by industry id
var industry = _.first(_.where(data.industry, matchIndustry));
// Filter by merchant id
var merchant = _.where(industry.merchant, matchMerchant);
_.each(merchant, function addToMatchResult(m){
match.push(m.name);
});
});
return match;
}
var filteredData = filterByCriteria(select, data);
From snippet above, any merchants which match the search criteria will be taken to the match list. Is it more readable to you?
Do you even need numerical id's? Gets super easy when you don't.
/*
{
"industry": {
"oil and gas":{
"merchant": {
"ABC": {
"name": "ABC oil"
},
"DEF": {
"name": "DEF gas"
},
"GHJ" :{
"name": "GHJ oil and gas"
}
}
},
"IT": {
"merchant": {
"Apple" : {
"name": "Apple computers"
},
"HP": {
"name": "Hewlett Packard"
},
"Google": {
"name": "Google. Maw haw haw"
}
}
}
}
}
*/
var data = '{"industry": {"oil and gas":{"merchant": {"ABC": {"name": "ABC oil"},"DEF": {"name": "DEF gas"},"GHJ" :{"name": "GHJ oil and gas"}}},"IT": {"merchant": {"Apple" : {"name": "Apple computers"},"HP": {"name": "Hewlett Packard"},"Google": {"name": "Google. Maw haw haw"}}}}}';
data = JSON.parse(data);
var merchant = data.industry['IT'].merchant['HP'];
alert(merchant.name);
//console.log(merchant.name);
I am working on a solution where I need to search for an element in a deeply nested JSON by its id. I have been advised to use underscore.js which I am pretty new to.
After reading the documentation http://underscorejs.org/#find , I tried to implement the solution using find, filter and findWhere.
Here is what I tried using find :
var test = {
"menuInputRequestId": 1,
"catalog":[
{
"uid": 1,
"name": "Pizza",
"desc": "Italian cuisine",
"products": [
{
"uid": 3,
"name": "Devilled chicken",
"desc": "chicken pizza",
"prices":[
{
"uid": 7,
"name": "regular",
"price": "$10"
},
{
"uid": 8,
"name": "large",
"price": "$12"
}
]
}
]
},
{
"uid": 2,
"name": "Pasta",
"desc": "Italian cuisine pasta",
"products": [
{
"uid": 4,
"name": "Lasagne",
"desc": "chicken lasage",
"prices":[
{
"uid": 9,
"name": "small",
"price": "$10"
},
{
"uid": 10,
"name": "large",
"price": "$15"
}
]
},
{
"uid": 5,
"name": "Pasta",
"desc": "chicken pasta",
"prices":[
{
"uid": 11,
"name": "small",
"price": "$8"
},
{
"uid": 12,
"name": "large",
"price": "$12"
}
]
}
]
}
]
};
var x = _.find(test, function (item) {
return item.catalog && item.catalog.uid == 1;
});
And a Fiddle http://jsfiddle.net/8hmz0760/
The issue I faced is that these functions check the top level of the structure and not the nested properties thus returning undefined. I tried to use item.catalog && item.catalog.uid == 1; logic as suggested in a similar question Underscore.js - filtering in a nested Json but failed.
How can I find an item by value by searching the whole deeply nested structure?
EDIT:
The following code is the latest i tried. The issue in that is that it directly traverses to prices nested object and tries to find the value. But my requirement is to search for the value in all the layers of the JSON.
var x = _.filter(test, function(evt) {
return _.any(evt.items, function(itm){
return _.any(itm.outcomes, function(prc) {
return prc.uid === 1 ;
});
});
});
Here's a solution which creates an object where the keys are the uids:
var catalogues = test.catalog;
var products = _.flatten(_.pluck(catalogues, 'products'));
var prices = _.flatten(_.pluck(products, 'prices'));
var ids = _.reduce(catalogues.concat(products,prices), function(memo, value){
memo[value.uid] = value;
return memo;
}, {});
var itemWithUid2 = ids[2]
var itemWithUid12 = ids[12]
I dont use underscore.js but you can use this instead
function isArray(what) {
return Object.prototype.toString.call(what) === '[object Array]';
}
function find(json,key,value){
var result = [];
for (var property in json)
{
//console.log(property);
if (json.hasOwnProperty(property)) {
if( property == key && json[property] == value)
{
result.push(json);
}
if( isArray(json[property]))
{
for(var child in json[property])
{
//console.log(json[property][child]);
var res = find(json[property][child],key,value);
if(res.length >= 1 ){
result.push(res);}
}
}
}
}
return result;
}
console.log(find(test,"uid",4));