sorting json arrays in json array using angular js - json

I have json array in the following format,
$scope.data = [{
"values" : [["2 Day", 103.89], ["NextDay", 107.41], ["Ground", 428.75]],
"key" : "FedEx"
}, {
"values" : [["Ground", 117.8], ["NextDay", 10], ["2 Day", 15]],
"key" : "UPS"
}]
I need to sort it in to the following format :
$scope.data = [{
"values" : [["2 Day", 103.89], ["NextDay", 107.41], ["Ground", 428.75]],
"key" : "FedEx"
}, {
"values" : [["2 Day", 15], ["NextDay", 10], ["Ground", 117.8]],
"key" : "UPS"
}]
How can I do it using Angularjs?
A similar data set for which I want similar sorting to be applied, but here I have time (in long format) instead strings.
$scope.data1 = [{
"values" : [[1359072000000, 103.89], [1365116400000, 107.41], [1357516800000, 428.75]],
"key" : "FedEx"
}, {
"values" : [[1357516800000, 117.8], [1359072000000, 100], [1365116400000, 15]],
"key" : "UPS"
}];
To be formatted as
$scope.data1 = [{
"values" : [[1359072000000, 103.89], [1365116400000, 107.41], [1357516800000, 428.75]],
"key" : "FedEx"
}, {
"values" : [[1359072000000, 100],[1365116400000, 15], [1357516800000, 117.8], ],
"key" : "UPS"
}];

Natural sorting can be applied in js like this. Natural sorting is required since strings in your array contains numbers.
function strcmp(a, b) {
return a > b ? 1 : a < b ? -1 : 0;
}
function natcmp(a, b) {
var x = [], y = [];
a[0].replace(/(\d+)|(\D+)/g, function($0, $1, $2) { x.push([$1 || 0, $2]) })
b[0].replace(/(\d+)|(\D+)/g, function($0, $1, $2) { y.push([$1 || 0, $2]) })
while(x.length && y.length) {
var xx = x.shift();
var yy = y.shift();
var nn = (yy[0]-xx[0]) || strcmp(yy[1],xx[1]);
if(nn) return nn;
}
if(x.length) return -1;
if(y.length) return +1;
return 0;
}
Apply sorting in your array using javascript sort function as shown below.
$scope.data = $scope.data.map(function(d){ d.values = d.values.sort(natcmp); return d; });
Natural sorting is not needed for the second dataset. To sort the array in descending order by time, try this.
$scope.data1 = $scope.data1.map(function(d) {
d.values = d.values.sort(function(a, b) {
return new Date(b[0]) - new Date(a[0])
});
return d;
});

for displaying the array with some ng-repeat, you could use a filter
https://docs.angularjs.org/api/ng/filter/filter
There is orderBy filter but you can create your own filters.
for the data model to be sorted and not only for presentation, you could use the javascript sort function for arrays and give it a sorting implementation which compares 2 elements.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Related

How to fetch value from json object in angular 6?

I have one json file which contains multiple objects inside another object. I want to fetch data but not using key of that value. I want to iterate there key and values and want to print them dynamically in angular 6.
{
"name" : "abc",
"tags" : "def",
"updated-by" : "ijk",
"property" : {
"description" : "abcd",
"type" : "string"
},
"sources" : {
"input" : {
"type" : "lmn",
"properties" : {
"key" : "opq"
}
}
}
}
Can we iterate objects like we iterates array. If anyone can help?
I would suggest referring this StackOverflow question,
As far as I know, *ngFor can be used not only for arrays but also for Objects.
Hope the above link helps.
Also for the keys whose values contain objects, you could check if the corresponding value of the key is an object.
For instance,
if( (typeof A === "object") && (A !== null) )
where A is the corresponding value of the key. If A is indeed an object, use *ngFor again to iterate over the object.
I haven't tested the following code but I hope you get an overview of what I am trying to say,
#Component({
selector: 'app-myview',
template:
`<div *ngFor="let key of objectKeys(items)">{{key + ' : ' + items[key]}}
<div *ngIf="checkFunction(items[key])">
<div *ngFor="let key2 of objectKeys(items[key])">
{{key2 + ' :' + items[key][key2]}}
</div>
</div>
</div>`
})
export class MyComponent {
objectKeys = Object.keys;
items = { keyOne: 'value 1', keyTwo: 'value 2', keyThree: 'value 3' };
constructor(){}
checkFunction(obj){
if( (typeof obj === "object") && (obj !== null) )
{
return true;
}
else{
return false;
}
}
}
var temp = {
"name" : "abc",
"tags" : "def",
"updated-by" : "ijk",
"property" : {
"description" : "abcd",
"type" : "string"
},
"sources" : {
"input" : {
"type" : "lmn",
"properties" : {
"key" : "opq"
}
}
}
};
flatten the object
var flattenObject = function(ob) {
var toReturn = {};
for (var i in ob) {
if (!ob.hasOwnProperty(i)) continue;
if ((typeof ob[i]) == 'object') {
var flatObject = flattenObject(ob[i]);
for (var x in flatObject) {
if (!flatObject.hasOwnProperty(x)) continue;
toReturn[i + '.' + x] = flatObject[x];
}
} else {
toReturn[i] = ob[i];
}
}
return toReturn;
};
var flat = flattenObject(temp)
Iterate over object just like array
Object.entries(flat).forEach(entry => {
console.log(entry[0] + " => " + entry[1])
})

Return flat object from sequelize with association

I am working on converting all my queries in sequelize.
The problem I have come across is that when select queries include associations (ex. one to many), the object I get is an array of nested objects.
It looks something like:
[
{
"field1": "someval",
"field2": "someval1",
"assoc_table": {
"field_a": 1,
"field_b": "someval"
}
},
{
"field1": "someval",
"field2": "someval3",
"assoc_table": {
"field_a": 5,
"field_b": "someval"
}
},
{
"field1": "someval",
"field2": "someval3",
"assoc_table": {
"field_a": 12,
"field_b": "someval"
}
}
]
I tried to use different modules to flatten the objects (inside a loop, each object individually), but I always got an error telling that what I was trying to flatten were not just objects.
Moreover, I would prefer avoiding the part where objects are flattened, and simply get a flat result with sequelize.
The sequelize code looks something like this:
models.table1.findAll({
attributes: ['field1', 'field2'],
where: {field1: someval},
include: [{model: models.assoc_table, required: true, attributes:['field_a', 'field_b']}]
}).then(function (result) {
res.send(result);
}).catch(function(error) {
console.log(error);
});
Part of your issue is probably that your result is an array of model instances, so you might be having issues flattening it if you didn't call toJSON on the elements in the array. I provided code that would flatten your example:
result.forEach(obj => {
Object.keys(obj.toJSON()).forEach(k => {
if (typeof obj[k] === 'object') {
Object.keys(obj[k]).forEach(j => obj[j] = obj[k][j]);
}
});
});
You can also add raw: true as an option to findAll, which will flatten your object, but it will look like this:
[
{
"field1": "someval",
"field2": "someval1",
"assoc_table.field_a": 1,
"assoc_table.field_b": "someval"
},
...
]
Old question, but as I was attempting to do this also and found a pure sequelize solution that does not require the "after" mapping, I wanted to add that here. So to have sequelize itself return the desired object format, it would be this, where the attributes are explicitly assigned based off column return values from the include table:
models.table1.findAll({
attributes: [
'field1',
'field2',
[sequelize.col('models.assoc_table.field_a'), 'field_a'], // Set key
[sequelize.col('models.assoc_table.field_b'), 'field_b'], // Set key
],
where: {field1: someval},
include: [
{model: models.assoc_table,
required: true,
attributes:[], // Explicitly do not send back nested key's
}
]
})
old question, but with new sequelize updates, use both:
raw:true,
nest:true
Using raw: true a helper function can simplify the return keys. This will make sure no values are over-written and gives a way to keep some of string-based nesting (IDs for example).
/**
Simplify keys returned by a sequelize {raw: true} query. Makes sure no values
are over-written and gives a way to keep some of string-based nesting (IDs for
example).
#example result.map(r => trimKeys(r))
*/
function trimKeys(obj, deepin = ['id']) {
const keys = Object.keys(obj)
const ret = {}
for (var i = 0; i < keys.length; i++) {
const key = keys[i]
const keyParts = key.split('.')
let idx = 1
let newKey = keyParts[keyParts.length - idx]
while((ret[newKey] || deepin.find(d => newKey === d)) && idx >= 0) {
idx++
newKey = keyParts[keyParts.length - idx] + '.' + newKey
}
ret[newKey] = obj[key]
}
return ret
}

Group bar chart data not working as expected

Have an array set of the type json object.
Var data has an array object like:
0: Object
letter : A
frequency : .08167
1: Object
letter : B
frequency : .01492
2: Object
letter : C
frequency : .02780
3: Object
letter : A
frequency : .06167
4: Object
letter : D
frequency : .02492
5: Object
letter : C
frequency : .03780
The field letter has more than one set of data(for ex: letter A is having two sets of frequency or more). I need to create a group chart for the same.
var frequency= d3.keys(data[0]).filter(function (key) {
return (key !== "letter");
});
data.forEach(function (d) {
d.val = frequency.map(function (name) { return { name: name, value: +d[name] }; });
});
This doesn't seem to work. Is there anything wrong in the structure of Json data?
From the question and comment, it seems like you have your data in a format like this:
data = [
{ "letter": "A", "frequency": .08167, "year": 2015},
{ "letter": "B", "frequency": .01492, "year": 2015},
{ "letter": "C", "frequency": .02780, "year": 2015}...
In which case, the best way to group by letter for your grouped bar chart would be to use d3.nest:
var nested_data = d3.nest()
.key(function (d) {
return d.letter;
})
.entries(data);
which will structure your data with multiple values under each letter key.
Here's a working fiddle with your data: http://jsfiddle.net/henbox/jc0nohhb/1/, that borrows a lot from this example: http://bl.ocks.org/mbostock/3887051
Note - This is a good resource to read more about nest in d3: http://bl.ocks.org/phoebebright/raw/3176159/

Searching in JSON for specific value using AngularJS

I am trying to filter my JSON object by a specific property value set to Log: true
If an object has this property set to false, I want to filter it out. Here is an example of the JSON structure:
$scope.Main =
{
"MyBook" :
{
"Title": "The Road",
"Type" : "Text",
"Log" : false
},
"MyCat":
{
"Name" : "Penny",
"Type" : "Pet",
"Log" : true
},
"Car":
{
"Make" : "Toyota",
"Model" : "Camry",
"Type" : "Vehicle",
"Log" : false
}
}
As you can see, the objects themselves are not similar, but they all contains a log property.
Online Demo
This is how I would filtered an object while searching for a property value equals true
var sampleObj = {/* you sample object*/};
var filtered = Object.keys(sampleObj).reduce(function(arr,prop){
if(Object.keys(sampleObj[prop])
.filter(function (p) {return p === "Log";})){
if(sampleObj[prop].Log==true){
arr.push(sampleObj[prop]);
}
}
return arr;
},[]);
console.log(filtered);
Since you are using angular probably you would want to use a custom filter instead:
Something close to:
custom filter:
angular.module('myApp', []).filter('myFilter', function() {
return function(sampleObj, param1) {
return Object.keys(sampleObj).reduce(function(arr,prop){
if(Object.keys(sampleObj[prop])
.filter(function (p) {return p === "Log";})){
if(sampleObj[prop].Log==param1){
arr.push(sampleObj[prop]);
}
}
return arr;
},[]);
};
});
and in your html
<li ng-repeat="item in sampleObj | myFilter: true">
try using the underscorejs library.
you can use some of their functions like _.filter and _.has to filter the list.
here's an example of how i would try to implement that object:
var filtered = _.filter($scope.Main, function(obj) {
return _.has(obj, "Log") && obj.Log;
}
Use a custom Angular filter:
.filter('filterLog', function(){
return function(items){
for (var item in items) {
if (items[item].Log === false) delete items[item];
}
return items;
}
})
Then, in your view, you could output the filtered list like so:
<li ng-repeat="(key, value) in Main | filterLog">{{value}}</li>
If you need to use it in a controller, you could:
$scope.filtered = $filter('filterLog')($scope.Main);
Demo

JSON is reordering returned list

I have some jQuery that will change the contents of a select box depending upon the value of another select box (it uses AJAX). The PHP is returning the array how I want (sorted by TypeDescriptionData.name), but the list is getting re-ordered somewhere in the JS. Any ideas?
PHP Call:
public function get_descriptions_by_type($typeId) {
$this->autoRender = false;
$this->loadModel('TypeDescriptionData');
$data = array();
$this->TypeDescriptionData->contain();
$descriptions = $this->TypeDescriptionData->find('list', array('conditions' => array('type_data_id' => $typeId), 'order' => 'name ASC'));
if(isset($descriptions) && is_array($descriptions)) {
foreach($descriptions as $x => $y) {
$data[$x] = $y;
}
}
echo json_encode($data);
}
Here's the JSON right after the json_encode php call above:
{
"1":"FD 50",
"9":"Hypercom T4210",
"2":"Hypercom T7P",
"8":"Hypercom T7Plus",
"10":"Nurit 2085",
"11":"Nurit 8400",
"12":"Nurit 8400 Lite",
"17":"Other Terminal",
"13":"Verifone Tranz 330",
"14":"Verifone Tranz 380",
"15":"Verifone Vx510",
"16":"Verifone Vx510 LE"
}
Here's the JS:
$('#TypeType').change(function() {
$('#TypeDescription').find('option').remove().end();
$.ajax({
url:'/TypeData/get_descriptions_by_type/' + $(this).val(),
type:'POST',
dataType: 'json',
success: function( json ) {
console.log(JSON.stringify(json));
$.each(json, function(i, value) {
$('#TypeDescription').prepend($('<option>').text(value).attr('value', i));
});
}
});
});
And here's the JSON if I add "console.log(JSON.stringify(json));" immediately after the "success: function( json ) {" in the JS above:
{
"1":"FD 50",
"2":"Hypercom T7P",
"8":"Hypercom T7Plus",
"9":"Hypercom T4210",
"10":"Nurit 2085",
"11":"Nurit 8400",
"12":"Nurit 8400 Lite",
"13":"Verifone Tranz 330",
"14":"Verifone Tranz 380",
"15":"Verifone Vx510",
"16":"Verifone Vx510 LE",
"17":"Other Terminal"
}
A json object is an associative array, so the two objects are actually equivalent. Basically the computer doesn't remember the order, just key/value pairs. If order really matters to you then you need to use some other kind of structure. For example you could embed an array:
{
"beaches" : [
{"key" : "1", "value" : "FD 50"},
{"key" : "2", "value" : "Hypercom T7P"},
{"key" : "8", "value" : "Hypercom T7Plus"}
]
}