angular / angular.js

AngularJS - HTML enhanced for web apps!
https://angularjs.org
MIT License
58.82k stars 27.51k forks source link

Introduce relation algebra to objects manipulation by extendeding to | orderBy and | filter #11175

Closed lilan123 closed 9 years ago

lilan123 commented 9 years ago

it will be a great feature to have relation algebra and lambda expressions (extended to | orderBy and | filter) to object manipulation in angularJs. Specially it will be helped to developers who worked with third party API and stuff. This will be used all most all.

Narretz commented 9 years ago

Can you give / show an example? I don't really understand how this would work, or actually what you mean.

lilan123 commented 9 years ago

Please find high level of the idea as below

ex: 1 $scope.obj1={id:001, name:jone}; $scope.obj2=[{empid:001, employeeType: permanent },{empid:002, employeeType: permanent }]

Expect : $scope.obj3={id:001,name:jone, empid:001, employeeType: permanent}

obj1 JOIN obj2 | where: obj1 .id==obj2.empid

ex: 2 $scope.obj2=[{Name:Harry, EmpId: 3415, DeptName:Finance},{Name:Sally, EmpId: 2241, DeptName:Sales},{Name:George, EmpId: 3401, DeptName:Finance}]

Expect : $scope.obj3=[{Name:Harry, EmpId: 3415, DeptName:Finance}, {Name:George, EmpId: 3401, DeptName:Finance}]

obj1 | where: obj1 .id=='Finance'

By this we will be able to use Projection, Join (Outer joins, Right outer join, Full outer join), Division and also addition to this it can be introduced sum, average, etc...

ex: 3 sum(e.salary) e in employees | where: e.salary>25000

Lambda expressions sample as below

ex: 4 $scope.numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; $scope.oddNumbers = $scope.numbers.Count(n => n % 2 == 1); $scope.firstNumbersLessThan6 = $scope.numbers.TakeWhile(n => n < 6);

Lambda expressions Where $scope.customers.Where(c => c.City == "London");

it will be helped developers to get above functions from the core itself rather than developing their own logic and much user friendly framework everyone loved to use, everything in one place and easy to use.

gkalpak commented 9 years ago

(Most of) what you describe can be easily achieved with filters. Although, I understand it might be very useful in specific cases, I don't feel like it is something that belongs in core (also considering how easy it is to achieve the same functionality with an external module). Additionally, I don't see how having it implemeted in core would add benefit in regard with performance.

Defining these features/filters in a module and including that as a dependency in any app you might need it, seems to be the way to go. (Publishing the module would also help other developers that might need similar functionality :))

lilan123 commented 9 years ago

would you be able to give a sample how to achieve this by filters ??? of course if that is the case i would develop myself and publish it here. :)

gkalpak commented 9 years ago

@lilan123, some things can already be accomplished (either using native JS or existing Angular stuff). Below is a rough version of how I would approach your examples above. There is also this demo fiddle.

There is a chance I have not understood your requirements correctly, so let me know.

(I have kept things ES5-y, but it can get shorter by using ES6 goodies.)

Example 1
---------
  Input:
    $scope.obj1 = {id: '001', name: 'John'};
    $scope.obj2 = [
      {empId: '001', employeeType: 'permanent'}, 
      {empId: '002', employeeType: 'permanent'}
    ];
  Output
    $scope.obj3 = {id: '001', name: 'John', empId: '001', employeeType: 'permanent'};
  Proposed syntax:
    obj1 JOIN obj2 | where: obj1.id === obj2.empId
  Suggested solution:
    Option 1: {} | extend:obj1:(obj2 | filter:{empId: obj1.id}:true)[0]
    Option 2: (obj1 | joinWhere:obj2:'@id':'@empId')[0]   // <-- to retrieve the first "row"

  Implemented filters:
    function extendFilter() {
      var slice = Array.prototype.slice;
      return function _doFilter() {
        return angular.extend.apply(null, slice.call(arguments));
      };
    }
    function joinWhereFilter() {
      function createValueGetter(value) {
        return (angular.isString(value) && (value.charAt(0) === '@')) ?
          function (obj) { return obj[value]; } :
          function () { return value; };
      }
      return function _doFilter(obj1, obj2, prop1, prop2) {
        var valueGetter1 = createValueGetter(prop1);
        var valueGetter2 = createValueGetter(prop2);
        var targetValue = valueGetter1(obj1);
        var matching = obj2.filter(function (item) { return valueGetter2(item) === targetValue; });
        var joined = matching.map(function (item) { return angular.extend({}, obj1, item); });
        return joined;
      }
    }

Example 2
---------
  Input:
    $scope.obj1 = [
      {name: 'Harry', empId: 3415, deptName: 'Finance'},
      {name: 'Sally', empId: 2241, deptName: 'Sales'},
      {name: 'George', empId: 3401, deptName: 'Finance'}
    ];
  Output:
    $scope.obj2 = [
      {name: 'Harry', empId: 3415, deptName: 'Finance'},
      {name: 'George', empId: 3401, deptName: 'Finance'}
    ];
  Proposed syntax:
    obj1 | where: obj1.deptName === 'Finance'
  Suggested solution:
    obj1 | filter:{deptName: 'Finance'}:true

Example 3
---------
  Proposed syntax:
    sum(e.salary) e in employees | where: 'e.salary>25000'
  Suggested solution:
    employees | where:'salary>25000' | sum:'salary'

  Implemented filters:
    function sumFilter() {
      return function _doFilter(arr, prop) {
        return arr.reduce(function (acc, item) { return acc + item[prop]; }, 0);
      };
    }
    function whereFilter($parse) {
      return function _doFilter(arr, expr) {
        var evaluateExprFn = $parse(expr);
        return arr.filter(function (item) { return evaluateExprFn(item) === true; });
      };
    }

Example 4
---------
  Input:
    $scope.numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
  Proposed syntax:
    $scope.oddNumbers = $scope.numbers.count(n => n % 2 == 1);
    $scope.firstNumbersLessThan6 = $scope.numbers.takeWhile(n => n < 6);
    $scope.customers.where(c => c.city == "London");
  Suggested solution:
    $scope.oddNumbers = $scope.numbers.filter(function (n) { return n % 2 === 1; });
    $scope.firstNumbersLessThan6 = ... // No easy solution for that (you need more than one line; can write a helper.)
    $scope.inLondon = $scope.customers.filter(function (c) { return c.city === 'London'; });
lilan123 commented 9 years ago

Woow, this is really good... Thanks a lot @gkalpak. well, now it feels like me to do a module here. :)

pkozlowski-opensource commented 9 years ago

Based on the latest comment from opener and prior discussion I'm going to close this one as "not in core".