0

This is how my array looks like: and my array is not type of string, it's type of it own object called MyObject (just like setter and getter objects in java)

["Car","model","year","color","price"]
["Table","model","year","color","price"]
["Car","model","year","color","price"]
["Car","model","year","color","price"]
["Laptop","model","year","color","price"]
["Laptop","model","year","color","price"]

now I want to group by this in typescript and count how many of that item exist in array (also like in sql)

name  |count
 Car  | 3
Laptop| 2
Table | 1

and in my typescript file I have this

private groupByObjects() {
  //this read all data in array allData[] from service
  this.dataService.retrieveData().subscribe(allData => {

});

}

Could anyone please help me to write this in typescript?

5 Answers 5

1

I tried a lot and can write the answer which is works for me perfectly. So I post it now and hope it helps anyone who has the same problem as mine.

private groupByElement(receivedData: ReceivedData, elements: Array<GroupByElement>) {
    let groupElement: GroupByElement = new GroupByElement;

    if (!elements.find(x => x.element== receivedData.element)) {
      groupElement.element= receivedData.element;
      groupElement.count = 1;
      elements.push(groupElement);
    } else {
      this.updateElementCounter = elements.find(x => x.element== receivedData.element)?.count;
      this.updateElementCounter! += 1;
      this.indexElementCount = elements.findIndex(x => x.element== receivedData.element);

      elements[this.indexElementCount].count = this.updateElementCounter!;
    }
  }
Sign up to request clarification or add additional context in comments.

Comments

0

Why not use lodash ?

u can get this with only 2 lines :

private groupByObjects() {
  //this read all data in array allData[] from service
  this.dataService.retrieveData().subscribe(allData => {
  const counts = _.countBy(allData, '[0]');
  const data = _.unionBy(allData, '[0]');
  console.log(counts, data);
});

do not forget to install and import lodash :

npm install -s lodash

import * as _ from 'lodash';

4 Comments

Why? This just makes your bundle size larger for no reason.
import { countBy, unionBy } from 'lodash';
Still, why. There's no reason to add an additional dependency for one use case...
I don't want to use a library for this but the library is javascript and I need typescript
0

I like the approach of a list reducer using a function that return another function. That way you can always specify which key to use, in your case this key is 0.

interface PropObject {
  [index: string]: number;
}

const groupByCounter = (key : number) => (result : PropObject ,current : string []) => {

  result[current[key]] = result[current[key]] ? result[current[key]] + 1 : 1;
  return result;
};

const data : string [][] = [["Car","model","year","color","price"],
["Table","model","year","color","price"],
["Car","model","year","color","price"],
["Car","model","year","color","price"],
["Laptop","model","year","color","price"],
["Laptop","model","year","color","price"]];

const group = data.reduce(groupByCounter(0),{});
console.log(group);

Link to typescript playground.

Check out the javascript version

4 Comments

Some of this code is redundant (checking if the type is "undefined"). Undefined values are falsy by nature, so result[key] suffices. Also the typeof operator is redundant. If you want to explicitly checks if undefined, result[key] == undefined suffices.
@Urmzd I have updated the code
@DavidLemon could you please help me to fix this problem? this is the error I get No overload matches this call. Argument of type '(result: PropObject, current: string[]) => PropObject' is not assignable to parameter of type '(previousValue: allData, currentValue: allData, currentIndex: number, array: allData[]) => allData'. Types of parameters 'result' and 'previousValue' are incompatible. Type 'allData' is not assignable to type 'PropObject'. Index signature for type 'string' is missing in type 'allData'. my array is my object type allData: myObject[][]
This code is also very dangerous. Mutating objects directly is never recommended..
0

You can do this in O(n) through a simple reduction

In your case,

type ApplyFunction<X, T, R> = (all: X, current: T) => R
type GetFunction<T,R> = (t: T) => R

const groupAndApplyByIndex = <T, V extends string | number, R>(data: Array<T>, get: GetFunction<T,V>, apply: ApplyFunction<Record<V, R>, V, R>) => {
  return data.reduce((all, element) => {
    return {
      ...all,
      [get(element)]: apply(all, get(element))
    }
  }, {} as Record<V, R>)
}

And just use the above function in your code by

private groupByObjects() {
  //this read all data in array allData[] from service
  this.dataService.retrieveData().subscribe(allData => {

    const getX: GetFunction<string[], string> = (t) => t[0]
    const counts = groupAndApplyByIndex(allData, getX, (all, element) => ((all[element] as number || 0) + 1))

    // Rest of your logic...
  });

By using the above method, you have a function which can count or do other transformations that you define through apply

TypeScript Playground

6 Comments

I need typescript not javascript
you are correct but there is one problem, my array is not type of string it's` myObject` but when I change the code it gives me error that it can't be anything except string or number
This information is not specified in the question. What is the type of myObject @SpacemanSps
it's a class that contain data like in java set and get class, so all data that read will be assign to this class because the input data I get is Json and that works pretty good except here
Can you show the class responsible? Are set and get always present? I need a MRE to help :)
|
0

Given that you are already using rxjs, I would stay using that. I don't know if you'll think it's the best answer, and I didn't test it so you might have to debug, but I find this solution fun.

type Item = [string, string, string, string, string]; // you should make this tighter
type ItemCount = [string, number];

const source$: Observable<Item[]> = of([
  ["Car","model","year","color","price"],
  ["Table","model","year","color","price"],
  ["Car","model","year","color","price"],
  ["Car","model","year","color","price"],
  ["Laptop","model","year","color","price"],
  ["Laptop","model","year","color","price"]
]); // mock of this.dataService.retrieveData()

source$.pipe(
  mergeMap(res => from(res)), // emit each item one at a time
  groupBy((item: Item) => item[0]),
  mergeMap((item$: GroupedObservable<Item>) => item$.pipe( // item$ is an observable of items were each item's first element equals item$.key
    count(), // wait until complete, then emit count
    map<number, ItemCount>(c => [item$.key, c]) // turn count into tuple of [key, count]
  )),
  toArray(), // Wait until each item$ has been converted, then emit the final ItemCount[]
  map((itemCounts: ItemCount[]) => new Map<string, number>(itemCounts)) // you could do Object.fromEntries(itemCounts) instead. I like map objects
).subscribe(mapOfCountsPerKey => {
    // whatever you want to do
});

7 Comments

I need typescript not javascript
... that is Typescript.
I converted my data to Item[] but there is nothing called pipe and it says this doesn't exist
But you are using rxjs, right? Did you skip the of()?
I didn't include the imports. You'll need lines at the top of your script with something like import { of, from } from 'rxjs'; and import { count, groupBy, mergeMap, map, toArray } from 'rxjs/operators';
|

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.