2

The response from my http call is an array of company objects like this:

response = [{
    "name": "CompanyName1",
    "registrations": [
      {
        "country": "en",
        "register": "registerType1",
        "amount": 2
      },
      {
        "country": "wa",
        "register": "registerType2",
        "amount": 1
      },{
        "country": "en",
        "register": "registerType1",
        "amount": 3
      },
       {
        "country": "sc",
        "register": "registerType3",
        "amount": 2
      },
      {
        "country": "wa",
        "register": "registerType3",
        "amount": 2
      }
    ]
  },
  {
    "name": "CompanyName2",
    "registrations": [
      {
        "country": "wa",
        "register": "registerType1",
        "amount": 3
      },
      {
        "country": "sc",
        "register": "registerType3",
        "amount": 1
      }
    ]]

I need to group/sum company's registrations by register and country into a new propertyName (registerType_12) if registerType1 || registerType2 otherwise just keep property name registerTypeN. that is for companyName1 I need to get a result like this:

CompanyName1: [{country:en, registerType_12, amount:5},
              {country:wa, registerTyoe_12, amount:1,
              {country:sc, registerType3, amount:4}
              }]
3
  • 1
    stackoverflow.com/help/formatting Commented Jun 12, 2017 at 13:46
  • Is this in a stream or a plain javascript object? Commented Jun 12, 2017 at 13:53
  • this is a stream coming from an Observable (TypeScript) Commented Jun 12, 2017 at 13:58

2 Answers 2

1

I made a Plunkr to demo how to do that: https://plnkr.co/edit/uO48fEVJGy2nwm587PO8?p=preview

Here's an explanation

I saved your data in a variable:

const { Observable } = Rx;

// FAKE DATA FROM BACKEND
const data = [
  {
    "name": "CompanyName1",
    "registrations": [
      {
        "country": "en",
        "register": "registerType1",
        "amount": 2
      },
      ...
    ]
  },
  {
    "name": "CompanyName2",
    "registrations": [
      {
        "country": "wa",
        "register": "registerType1",
        "amount": 3
      },
      ...
    ]
}];

I made a mock for a Response and Http.get method so in the end we have something that really looks like Angular code:
(These are not directly relevant for your problem)

// MOCK RESPONSE OBJECT
const response = () => {
  const r = {
    body: null
  };

  r.json = () => JSON.parse(r.body);

  return r;
};

// MOCK HTTP METHOD
const http = {
  get: (url) => {
    const res = response();
    res.body = JSON.stringify(data);

    return Observable.of(res).delay(1000)
  }
};

And here's how you could solve your problem in a clean way:

http
  .get('some-url')
  .map(res => res.json())
  .map(groupByCompanyNameAndRegistration)
  .do(data => console.log(data))
  .subscribe();


Oubviously, the groupByCompanyNameAndRegistration is not made yet. But we have a clean chain of Observable and it's really easy to understand what's going on.

The logic part of the code:

const flattenObj = (obj) =>
  Object.keys(obj).map(key => obj[key]);

const groupByCompanyNameAndRegistration = (data) => {
  return data.reduce((accComp, company) => {
    if (!accComp[company.name]) {
      accComp[company.name] = {};
    }

    const compObj = company.registrations.reduce((accReg, reg) => {
      if (!accReg[reg.country]) {
        accReg[reg.country] = {
          country: reg.country,
          amount: 0
        };
      }

      accReg[reg.country].amount += reg.amount;

      return accReg;
    }, {});

    accComp[company.name] = flattenObj(compObj);

    return accComp;
  }, {});
};

And finally, the console output :

{
  "CompanyName1": [
    {
      "country": "en",
      "amount": 5
    },
    {
      "country": "wa",
      "amount": 3
    },
    {
      "country": "sc",
      "amount": 2
    }
  ],
  "CompanyName2": [
    {
      "country": "wa",
      "amount": 3
    },
    {
      "country": "sc",
      "amount": 1
    }
  ]
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks a lot! This solution is also brilliant, clean and correct for the RxJS approach, but I realized that I have to keep the original response in the view, I decided to create an Angular Pipe and just pass the data.registration property array to the Pipe so I use the @skeptor solution
Thanks for the kind words! No problem. If you go with the pipe solution, keep in mind to use a pure pipe to avoid computing again and again, at every change detection the same values. Maybe try to console.log something in your pipe and if it's called too many times watch out for pure or go with my solution that is done once and for all ;)
I am very sure I will use it in a future similar problem, you gave me great ideas from your answer, thanks!
0

You don't need typescript or rxjs for this. It can be achieved with pure javascript.

JSBin link : https://jsbin.com/yujocofesu/edit?js,console

response.reduce((finalObj, obj)=>{
     finalObj[obj.name] = 
         obj.registrations
             .map(reg => Object.assign(reg,
                  {register: ( 
                     reg.register ==='registerType1'||
                     reg.register==='registerType2') ?
                     'registerType_12': reg.register 
                   })
              )
              .reduce(mergeSimilar, []);
     return finalObj;
   },{});


function mergeSimilar(newList, item){
  let idx = newList.findIndex( 
      i=> i.country === item.country && i.register === item.register);
  if(idx>-1){
    newList[idx].amount += item.amount;
  }else{
    newList.push(item)
  }
  return newList;
}

This gives response as

{
  CompanyName1: [{
    amount: 5,
    country: "en",
    register: "registerType_12"
  }, {
    amount: 1,
    country: "wa",
    register: "registerType_12"
  }, {
    amount: 2,
    country: "sc",
    register: "registerType3"
  }, {
    amount: 2,
    country: "wa",
    register: "registerType3"
  }],
  CompanyName2: [{
    amount: 3,
    country: "wa",
    register: "registerType_12"
  }, {
    amount: 1,
    country: "sc",
    register: "registerType3"
  }]
}

3 Comments

Thanks @Skeptor, sorry by using an Angular Pipe I meant to use a javascript solution, actually your solution is perfect for the Pipe approach!!
I was just excited to see it in pure js , that's all :)
I simplified the problem just passing the response.registrations property to the Pipe, so I used a partial implementation of your algorithm but it is still a bit abstract for me;) I am learning to use this programming paradigm, thanks again for your solution

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.