具有rxjs.Observable属性的复杂对象

时间:2018-04-26 08:02:28

标签: typescript rxjs reactive-programming

我有一个复杂的javascript对象,例如:

{
  "@context": "http://schema.org",
  "@type": "Celebrity",
  "name": "Julius Caesar",
  "description": "translate:julius.amazing.bio",
  "address": {
    "@type": "PostalAddress",
    "addressLocality": "Roma",
    "postalCode": "9999",
    "streetAddress": "Coliseum Street 1",
    "addressCountry": "IT",
    "description": "translate:coliseum.amazing.bio"
  }
}

物体的深度几乎是无限的。现在必须翻译一些字段。在上面的示例中,它将是nameaddress.description

我的翻译功能有以下签名:

get(key: string): Observable<string>;

这里的内容是它返回一个Observable。目标是翻译以translate:开头的字段,并返回具有完全相同结构的对象。

我能想出的解决方案是forkJoin所有已翻译的字段,并在完成后更新对象。

const obj = {
    '@context': 'http://schema.org',
    '@type': 'Celebrity',
    'name': 'Julius Caesar',
    'description': 'translate:julius.amazing.bio',
    'address': {
        '@type': 'PostalAddress',
        'addressLocality': 'Roma',
        'postalCode': '9999',
        'streetAddress': 'Coliseum Street 1',
        'addressCountry': 'IT',
        'description': 'translate:coliseum.amazing.bio'
    }
};

const cesar$ = this.translate.get('julius.amazing.bio');
const coliseum$ = this.translate.get('coliseum.amazing.bio');

forkJoin([cesar$, coliseum$]).subscribe(res => {
    obj.description = res[0];
    obj.address.description = res[1];
});

它不雅,不灵活,代码需要为我拥有的每个对象重写。关于如何以优雅和可重复使用的方式(任何对象深度)完成此任务的任何建议,允许我翻译以translate:开头的任何字段?

2 个答案:

答案 0 :(得分:3)

您可以递归遍历整个对象并转换需要翻译的属性。然后,您需要收集数组中的所有可观察对象,以便能够使用forkJoin在所有翻译完成时具有可观察性。

我的快速刺杀,可能会有所改善:

import { delay, map} from 'rxjs/operators';
import { forkJoin } from 'rxjs/observable/forkJoin';
import { of } from 'rxjs/observable/of';
import { Observable } from 'rxjs/Observable';


const obj = {
    '@context': 'http://schema.org',
    '@type': 'Celebrity',
    'name': 'Julius Caesar',
    'description': 'translate:julius.amazing.bio',
    'address': {
        '@type': 'PostalAddress',
        'addressLocality': 'Roma',
        'postalCode': '9999',
        'streetAddress': 'Coliseum Street 1',
        'addressCountry': 'IT',
        'description': 'translate:coliseum.amazing.bio'
    }
};


let translate = {
    get(value: string) {
        return of("transalted " + value).pipe(delay(1000));
    }
}
const translatePrefix = "translate:"
function translateAll<T>(obj:T)  {
    let obs : Observable<void>[] = []
    for(let [key, value] of Object.entries(obj)) {
        if(typeof value === "object") {
            let o = translateAll(value).pipe(map(v=> { obj[key] = v }))
            obs.push(o);
        }
        else if(typeof value === "string") {
            if(!value.startsWith(translatePrefix)) continue;

            var translationKey = value.substr(translatePrefix.length);

            let o = translate.get(translationKey).pipe(map(v=> { obj[key] = v }));

            obs.push(o);
        }
    }

    return forkJoin(obs).pipe(map(v=> obj));
}

translateAll(obj).subscribe(v=> 
{
    console.log(v)
})

答案 1 :(得分:1)

正如@estus评论的那样,你可以递归遍历源对象来收集所有要翻译的字符串,然后翻译所有字符串,如下所示:

const translatePrefix = 'translate:';

// to return array of pairs:
// [string, function to apply result on object]

function getFields(obj) {
  const fields = [];
  Object.keys(obj).forEach(key => {
    const val = obj[key];
    if (typeof val === 'string' && val.startsWith(translatePrefix)) {
      fields.push([
        val.substr(translatePrefix.length),
        (translation, resultObj) => {resultObj[key] = translation;},
      ]);
    } else if (typeof val === 'object') {
      fields.push(... getFields(val).map(([toTranslate, apply]) => [
        toTranslate,
        (translation, resultObj) => {apply(translation, resultObj[key]);},
      ]));
    }
  });
  return fields;
}

function translateObj(obj) {
  return forkJoin(
    getFields(obj).map(
      ([str, apply]) => translate.get(str)
        .pipe(map(translation => apply.bind(translation)))
    )
  ).pipe(
    map(translationUpdates => {
      // clone obj to not modify source object (quick and dirty way)
      const res = JSON.parse(JSON.stringify(obj));
      // apply translations to it
      translationUpdates.forEach(apply => apply(res));
      return res;
    })
  );
}