问:
Am I creating proper unit tests with Angular and Jasmine?
Can you please give me feedback regards my recent unit tests with angular and Jasmine?
I am interested to develop my skills regard to writing better unit tests.

Should some of my test be in the e2e?
Can I write more unit tests for this component?
Should I combine some of the suits?

Any feedback will accept!

Thank you!

component.ts:

```<code>export class ReportDatasetCreationComponent implements OnInit, AfterViewChecked, OnDestroy {
  options: TableOptions = {
    sortable: true,
    filterable: true,
    navigateToEnd: true,
    callEvent: () => {
      this.data = [...this.data, { id: '', desc: '', type: 'str', values: '' }];
      this.inputsValid = true;
    },
    displayHeader: true,
    autoHeight: true,
    pagination: {
      length: 50,
      pageSize: 50,
      pageSizeOptions: [50, 100, 150, 200],
    }
  };
  loading = true;
  data: TableData = [];
  mainData;
  inputsValid;
  existingDatasets;
  columns = [];
  datasetDictionaries;
  // dictionaries;
  disableData;
  dataChanged = false;
  baseData;
  name = this.route.snapshot.params.reportName;
  duplicatedReport;
  show = false;
  showOriginRdsName = true;
  readOnly: boolean;
  tableDataObjChanged = false;
  indexOfElementToDelete;
  deleteHierarchyResponse;
  columnLinkedHierarchies = [];

  constructor(
    private route: ActivatedRoute,
    public dialog: MatDialog,
    public snackBar: MatSnackBar,
    private router: Router,
    private reportDatasetsService: ReportDatasetsService,
    public dictionaryService: DictionaryService,
    public datasetsService: DatasetsService,
    private authenticationService: AuthenticationService,
    public notificationsService: NotificationsService
  ) {
  }

  getColumns() {
    return [
      {
        key: 'id', label: 'Column', type: TABLE_COLUMNS_TYPES.INPUT, config: {
          validators: [this.isEmpty, this.isNotUnique],
          disabled: this.isDisabled
        }
      },
      {
        key: 'desc', label: 'Description', type: TABLE_COLUMNS_TYPES.INPUT, cellStyles: { 'width': '30%' }, config: {
          disabled: this.isDisabled
        }
      },
      {
        key: 'type', label: 'Type', type: TABLE_COLUMNS_TYPES.ROW_SELECT, options: this.datasetDictionaries, config: {
          validators: [this.isEmpty],
          disabled: this.isDisabled
        }
      },
      {
        key: 'values', label: 'Applicable Values', type: TABLE_COLUMNS_TYPES.CHIPS, config: {
          disabled: this.isDisabled,
          isHierarchy: (row) => {
            return row.hierarchy ? true : false;
          }
        }
      },
      {
        key: 'manage-app-values',
        label: '',
        type: TABLE_COLUMNS_TYPES.ACTION,
        icon: 'sort',
        onClick: (element) => {
          if (!this.readOnly) { this.openHierarchyModal(element); }
        },
        config: {
          disabled: this.isDisabled,
          isRender: (row) => {
            return !this.isDisabled(row);
          }
        },
      },
      {
        key: 'delete',
        label: '',
        type: TABLE_COLUMNS_TYPES.ACTION,
        icon: 'delete',
        onClick: (element) => {
          if (!this.readOnly) { this.deleteRow(element); }
        },
        config: {
          disabled: this.isDisabled,
          isRender: (row) => {
            return !this.isDisabled(row);
          }
        },
      }
    ];
  }

  isNotUnique = (row, column) => {
    if (this.isDisabled(row)) {
      return null;
    }
    const value = row[column.key];

    const duplicate = this.data.find((dataRow: TableRow) => {
      return row !== dataRow && dataRow[column.key] === value;
    });

    return duplicate ? 'this field should be unique' : null;
  }

  isEmpty = (row, column) => {
    if (this.isDisabled(row)) {
      return null;
    }
    const value = row[column.key];

    return Boolean(value) ? null : '*required';
  }

  isDisabled = (row) => {
    return this.disableData.includes(row);
  }

  ngOnInit() {

    this.authenticationService.readOnly.pipe(first()).subscribe(res => {
      this.readOnly = res;
    });

    this.datasetDictionaries = this.dictionaryService.getDictionary('ReportDataSetDefinition.col_dtypes');
    this.columns = this.getColumns();
    if (!this.reportDatasetsService.isNewRDS) {
      this.getReportDatasetDetails();
    }

    this.reportDatasetsService.$reportDatasetDetails.subscribe((reportDatasetDetails: ReportDataset) => {
      if (reportDatasetDetails) {
        if (reportDatasetDetails['data_in_db_dt'] && reportDatasetDetails['data_in_db_dt'] !== 'NaT') {
          this.reportDatasetsService.data_in_db = true;
        } else { this.reportDatasetsService.data_in_db = false; }
        this.data = Object.assign(reportDatasetDetails.columns);
        this.disableData = this.data.filter((item: any) => {
          return DISABLED_ROWS_IDS.includes(item.id);
        });
        this.loading = false;
      }
    });

    this.reportDatasetsService.loadingStatus
      .subscribe(loadingStatus => {
        this.loading = !isResolved(loadingStatus);
      });

    this.duplicatedReport = this.reportDatasetsService.reportDatasetDetails;
    if ((!this.duplicatedReport) || (!this.duplicatedReport.hasOwnProperty('newName'))) {
      this.show = false;
      this.showOriginRdsName = true;
      this.duplicatedReport = null;
    } else {
      this.show = true;
      this.showOriginRdsName = false;
    }
  }

  ngAfterViewChecked() {

  }

  ngOnDestroy() {

    this.show = false;
    this.showOriginRdsName = true;
    if (this.tableDataObjChanged) {
      this.onSave();
    }
  }

  getReportDatasetDetails() {
    this.reportDatasetsService.getReportDatasetDetails(this.name).subscribe();
  }

  downloadStructure() {
    this.reportDatasetsService.downloadRDSStructure(this.name).subscribe(data => saveAs(data, this.name + '.csv'));
  }

  uploadStructure() {
    const dialogRef = this.dialog.open(UploadDatasetStructureComponent, {
      width: '700px',
      data: {
        datasetInfo: {
          name: this.route.snapshot.params.reportName
        },
      }
    });

    dialogRef.afterClosed().subscribe(result => {
      if (result) {
        const file = result['file'];
        const delimiter = result['delimiter'];
        const reader: FileReader = new FileReader();
        reader.readAsText(file);
        reader.onload = (e) => {
          const csv: string = reader.result as string;
          const lines = csv.split('\n');
          const array = [];
          for (let i = 1; i < lines.length; i++) {
            let arr = [];
            let line = lines[i].trim();
            let lastIndex = null;
            if (line !== '') {
              if (delimiter === ',') {
                if (line[line.length - 1] === ']') {
                  for (let j = line.length - 2; j > 0; j--) {
                    if (line[j] === '[') {
                      lastIndex = j;
                      line = line.slice(0, lastIndex) + '"' + line.slice(lastIndex) + '"';
                      break;
                    }
                  }
                }
                if (line[line.length - 1] === '"') {
                  for (let k = line.length - 2; k > 0; k--) {
                    if (line[k] === '"') {
                      lastIndex = k;
                      arr[3] = line.substring(lastIndex + 1, line.length - 1);
                      arr[3] = JSON.parse(arr[3].replace(/'/g, '"'));
                      line = line.substring(0, lastIndex - 1);
                      break;
                    }
                  }
                } else {
                  arr[3] = '';
                  line = line.substring(0, line.length - 1);
                }
                for (let l = line.length - 1; l > 0; l--) {
                  if (line[l] === ',') {
                    lastIndex = l;
                    arr[2] = line.substring(lastIndex + 1, line.length);
                    line = line.substring(0, lastIndex);
                    break;
                  }
                }

                lastIndex = line.indexOf(',');
                arr[0] = line.substring(0, lastIndex);
                arr[1] = line.substring(lastIndex + 1, line.length);
                if (arr[1].indexOf(',') > -1) {
                  arr[1] = arr[1].substring(1, arr[1].length - 1);
                }
              } else {
                arr = lines[i].split('|');
                arr[3] = arr[3].trim();
                if (arr[3] !== '') {
                  arr[3] = JSON.parse(arr[3].replace(/'/g, '"'));
                }
              }
              const obj = {
                id: arr[0],
                desc: arr[1],
                type: arr[2],
                values: arr[3]
              };
              array.push(obj);
            }
          }
          this.data = array;
          this.disableData = this.data.filter((item: any) => {
            return DISABLED_ROWS_IDS.includes(item.id);
          });
          this.setDataChanged();
        };
      }
    });

  }

  deleteRow(element) {
    this.dialog.open(DeleteConfirmationComponent).afterClosed().subscribe((isConfirmed: boolean) => {
      if (isConfirmed) {
        this.reportDatasetsService.saveReportDatasetDetails(this.data.filter((row) => row !== element)).subscribe(res => {
          if (!(res instanceof HttpErrorResponse)) {
            this.data = this.data.filter((row) => row !== element);
            this.resetBaseData();
            this.openSnackBar();
          }
        });
      }
    });
  }

  rowChange(e) {
    const isNotEmpty = disableButton(this.data, 'id');
    const dublicated = checkDuplicateInObject('id', this.data);
    if (isNotEmpty || dublicated) {
      this.inputsValid = true;
    } else {
      this.inputsValid = false;
    }
    this.setDataChanged();
  }

  openSnackBar() {
    const snackBarRef = this.snackBar.open('Report Dataset has been successfully saved', 'go back to RDS list', {
      duration: 2000
    });
    snackBarRef.onAction().subscribe(() => {
      this.router.navigate(['designer/report-datasets/']);
    });
  }

  goBack() {
    this.reportDatasetsService.isNewRDS = false;
    this.router.navigate(['designer/report-datasets/']);
  }

  onSave() {
    this.reportDatasetsService.saveReportDatasetDetails(this.data).subscribe(res => {
      if (!(res instanceof HttpErrorResponse)) {
        this.resetBaseData();
        this.openSnackBar();
      }
    });
  }

  openUploadModal() {
    this.dialog.open(UploadDatasetComponent, {
      width: '400px',
      data: {
        datasetInfo: {
          name: this.route.snapshot.params.reportName
        },
        isReportDataset: true
      }
    });
  }

  downloadFile() {
    this.reportDatasetsService.checkStatusCsvFile(this.name).subscribe((res) => {
      if (!(res instanceof HttpErrorResponse)) {
        this.reportDatasetsService.downloadRDScsvData(this.name).subscribe(data => saveAs(data, this.name + '.csv'));
      }
    });
  }

  setDataChanged() {
    this.dataChanged = !isEqual(this.data, this.baseData);
  }

  resetBaseData() {
    this.baseData = cloneDeep(this.data);
    this.setDataChanged();
  }

  iconContent(e) {
    if (e.fromElement === null) {
    } else if (e.fromElement.textContent === 'delete') {
      this.datasetsService.toolTipContent = 'Delete';
    } else if (e.fromElement.textContent === 'sort') {
      this.datasetsService.toolTipContent = 'Link/unlink hierarchy';
    }
  }

  openHierarchyModal(el) {

    const dialogRef = this.dialog.open(ManageApplicableValuesComponent, {
      width: '90%',
      data: {
        element: el,
        rdsName: this.route.snapshot.params.reportName,
        columnName: el.id,
        fromAction: 'rdsColumn'
      }
    });
    dialogRef.afterClosed().subscribe(result => {

      if (result === undefined) {
        // User clicked cancel or focus out from the modal

      } else if (result.action === 'save') {
        for (let i = 0; i < this.data.length; i++) {
          if (this.data[i].hierarchy === result.name) {
            const applicableValues = result.rowData.map(a => a.name);
            this.data[i].values = applicableValues;
          }
        }
        this.tableDataObjChanged = true;
        this.setDataChanged();

      } else if (result.action === 'unPairHierarchy') {
        this.data.find((o, i) => {
          if (o.id === el.id) {
            delete this.data[i].hierarchy;
            return true; // stop searching
          }
        });
        this.snackBar.open('Hierarchy un linked from column', null, { duration: 1000 });
        this.tableDataObjChanged = true;

      }

    });

  }

}
</code>```

spec.ts file:

```<code>fdescribe('ReportDatasetCreationComponent', () => {
  let component: ReportDatasetCreationComponent;
  let fixture: ComponentFixture<ReportDatasetCreationComponent>;
  let service;
  let mySpy;
  let httpMock;
  let de: DebugElement;

  let mockData = {
    columns: [
      {
        desc: 'Unique identifier of event. (Mandatory)',
        id: 'id',
        type: 'str'
      },
      {
        id: 'CURRENCY',
        type: 'str'
      },
      {
        id: 'NAME',
        type: 'str'
      },
      {
        hierarchy: 'coockie',
        id: 'COUNTRY',
        type: 'str',
        values: [
          'a',
          'b',
          'e',
          '123456789123456789123456789123456789123456789123456789123456789123456789'
        ]
      },
      {
        hierarchy: 'q',
        id: 'PRICE',
        type: 'nbr',
        values: [
          'q',
          '1',
          '2'
        ]
      }
    ],
    data_in_db_dt: '2020-01-06T23:21:46.375819',
    desc: '',
    filter_criteria: {},
    name: 'motoDealRds',
    updt_by: 'Shubby dubby',
    updt_on: '2020-01-06T23:21:46.376801'
  };

  let mockMusicShop = {
    columns: [
      {
        desc: 'Unique identifier of event. (Mandatory)',
        id: 'id',
        type: 'str'
      },
      {
        id: 'product',
        type: 'str'
      },
      {
        id: 'price',
        type: 'nbr'
      },
      {
        id: 'currency',
        type: 'str'
      },
      {
        id: 'manufacturer',
        type: 'str'
      }
    ],
    data_in_db_dt: '2020-02-12T19:03:16.755805',
    desc: 'RDS for unit testings',
    filter_criteria: {},
    name: 'music_shop',
    updt_by: 'Nikhil',
    updt_on: '2020-02-12T19:03:16.755805'
  }

  let mockColumnElement = {
    hierarchy: 'company_hierarchy_mock',
    id: 'PRICE',
    type: 'nbr',
    values: ['q', '1', '2']
  };

  let mockHierarchy = {
    name: 'mock hierarchy',
    rowData: [
      { name: 'a', path: ['a'], type: 'folder', id: 1 },
      { name: 'b', path: ['a', 'b'], type: 'folder', id: 2 },
      { name: 'c', path: ['a', 'c'], type: 'folder', id: 3 },
      { name: 'd', path: ['a', 'd'], type: 'folder', id: 4 }
    ]
  };

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [DesignerModule, RouterTestingModule, HttpClientTestingModule],
      providers: [
        AuthenticationService,
        DictionaryService,
        MockBackend,
        BaseRequestOptions,
        {
          provide: Http,
          useFactory: (mockBackend: MockBackend, defaultOptions: RequestOptions) => {
            return new Http(mockBackend, defaultOptions);
          },
          deps: [MockBackend, BaseRequestOptions]
        }
      ]
    })
      .compileComponents();
  }));

  beforeEach(() => {
    // ReportDatasetCreationComponent.prototype.ngOnInit = () => { };

    fixture = TestBed.createComponent(ReportDatasetCreationComponent);
    component = fixture.componentInstance;
    component.dictionaryService = new DictionaryServiceMock(TestBed.get(HttpClient));
    service = TestBed.get(ReportDatasetsService);
    httpMock = TestBed.get(HttpTestingController);
    de = fixture.debugElement;
    component.name = 'motoDealRds';
    mySpy = spyOn(service, 'getReportDatasetDetails').and.callThrough();
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();

  });

  it('should spyOn getReportDatasetDetails and return fake data', () => {

    service.$reportDatasetDetails.next(mockMusicShop);
    // this function should be called in ngOnInit once
    expect(service.getReportDatasetDetails).toHaveBeenCalledTimes(1);

    // Should enter the if condition after the response
    expect(component.reportDatasetDetailsResponse).toEqual(jasmine.objectContaining({
      data_in_db_dt: '2020-02-12T19:03:16.755805'
    }));

    // Since the response contain data, the data_in_db should be true:
    // expect(service.data_in_db).toBeTruthy();

  });

  it('Should return different results since the mockData will be change', () => {

    // Now, let's try to changed the data:
    service.$reportDatasetDetails.next(undefined);

    // this function should be called in ngOnInit once
    expect(service.getReportDatasetDetails).toHaveBeenCalledTimes(1);

    // The if condition should not pass and should not pass and go to the else scope:
    expect(service.data_in_db).toBeFalsy();

  });

  // mock a duplicate report and test the if conditions and the variables inside
  it('Should test the if conditions and the variables inside', () => {
    component.duplicatedReport = mockMusicShop;
    expect(component.show).toBeFalsy();
    expect(component.showOriginRdsName).toBeTruthy();
    expect(component.show).toBeFalsy();

  });

  it('Should call ngOnDestroy and test the state of the variables', () => {
    component.ngOnDestroy();
    expect(component.show).toBeFalsy();
    expect(component.showOriginRdsName).toBeTruthy();
    expect(component.tableDataObjChanged).toBeFalsy();

  });

  it('Should call ngOnDestroy and test the state of the variables, but this time, expect to call onSave once', () => {
    service.reportDatasetDetails = mockMusicShop;
    component.tableDataObjChanged = true;
    const onSaveSpy = spyOn(component, 'onSave').and.callThrough();
    component.ngOnDestroy();
    expect(onSaveSpy).toHaveBeenCalledTimes(1);

  });

  it('Should call onSave with mock data', () => {

    service.$reportDatasetDetails.next(mockMusicShop);
    service.reportDatasetDetails = mockMusicShop;
    const onSaveSpy = spyOn(component, 'onSave').and.callThrough();
    const saveReportDatasetDetailsSpy = spyOn(service, 'saveReportDatasetDetails').and.returnValue(of(null));
    const resetBaseDataSpy = spyOn(component, 'resetBaseData').and.callThrough();
    const openSnackBarSpy = spyOn(component, 'openSnackBar').and.callThrough();
    component.onSave();
    expect(onSaveSpy).toHaveBeenCalledTimes(1);
    expect(saveReportDatasetDetailsSpy).toHaveBeenCalledTimes(1);

    // Since the response is not en error object, it should call resetBaseData and openSnackBar

    expect(resetBaseDataSpy).toHaveBeenCalledTimes(1);
    expect(openSnackBarSpy).toHaveBeenCalledTimes(1);

  });

  it('Should call onSave with mock data, but this time the response will not pass the if condition', () => {

    service.$reportDatasetDetails.next(mockMusicShop);
    service.reportDatasetDetails = mockMusicShop;

    const onSaveSpy = spyOn(component, 'onSave').and.callThrough();
    const saveReportDatasetDetailsSpy = spyOn(service, 'saveReportDatasetDetails').and.callThrough();
    const resetBaseDataSpy = spyOn(component, 'resetBaseData').and.callThrough();
    const openSnackBarSpy = spyOn(component, 'openSnackBar').and.callThrough();

    component.onSave();

    expect(onSaveSpy).toHaveBeenCalledTimes(1);
    expect(saveReportDatasetDetailsSpy).toHaveBeenCalledTimes(1);

    // Since the response is undefined, it should not call resetBaseData and openSnackBar

    expect(resetBaseDataSpy).toHaveBeenCalledTimes(0);
    expect(openSnackBarSpy).toHaveBeenCalledTimes(0);

  });

});

</code>```

答:
Ok, I thought to put a comment on this question but since it is more than a para, I am putting it as an answer.

To start with, you can take a look at this series of articles which I wrote just for unit testing . There are links attached to it at the bottom of this article.

To summarize:

The main focus during unit testing it to make sure that we focus in isolating our component and test it functions and behavior.

You can't expect to cover all cases in unit test. To test how your components reacts while interacting with external events and dependent components, you can focus those points in e2e test cases.

In the provided code (which is way too long for me to provide feedback on each area of component),  I can say that you should primarily focus on whether the variables are initialized properly ,functions are being called, those functions are working properly , respective HTML changes are reflected accordingly. such as

this.columns

this.readOnly

whether getReportDatasetDetails() is called depending on this.reportDatasetsService.isNewRDS

this.data

this.showOriginRdsName

this.duplicatedReport

whether rowChange() is called and make expected changes

and so on...

Its near impossible to test private variable and functions

Please also look into branch and line coverage with its differences. Just writing more and more it block is also not a best practice. To mark the separate responsibility of unit and e2e testing is also important.

Body:
Can you please give me feedback regards my recent unit tests with angular and Jasmine?
I am interested to develop my skills regard to writing better unit tests.

Should some of my test be in the e2e?
Can I write more unit tests for this component?
Should I combine some of the suits?

Any feedback will accept!

Thank you!

component.ts:

export class ReportDatasetCreationComponent implements OnInit, AfterViewChecked, OnDestroy {
  options: TableOptions = {
    sortable: true,
    filterable: true,
    navigateToEnd: true,
    callEvent: () => {
      this.data = [...this.data, { id: '', desc: '', type: 'str', values: '' }];
      this.inputsValid = true;
    },
    displayHeader: true,
    autoHeight: true,
    pagination: {
      length: 50,
      pageSize: 50,
      pageSizeOptions: [50, 100, 150, 200],
    }
  };
  loading = true;
  data: TableData = [];
  mainData;
  inputsValid;
  existingDatasets;
  columns = [];
  datasetDictionaries;
  // dictionaries;
  disableData;
  dataChanged = false;
  baseData;
  name = this.route.snapshot.params.reportName;
  duplicatedReport;
  show = false;
  showOriginRdsName = true;
  readOnly: boolean;
  tableDataObjChanged = false;
  indexOfElementToDelete;
  deleteHierarchyResponse;
  columnLinkedHierarchies = [];

  constructor(
    private route: ActivatedRoute,
    public dialog: MatDialog,
    public snackBar: MatSnackBar,
    private router: Router,
    private reportDatasetsService: ReportDatasetsService,
    public dictionaryService: DictionaryService,
    public datasetsService: DatasetsService,
    private authenticationService: AuthenticationService,
    public notificationsService: NotificationsService
  ) {
  }

  getColumns() {
    return [
      {
        key: 'id', label: 'Column', type: TABLE_COLUMNS_TYPES.INPUT, config: {
          validators: [this.isEmpty, this.isNotUnique],
          disabled: this.isDisabled
        }
      },
      {
        key: 'desc', label: 'Description', type: TABLE_COLUMNS_TYPES.INPUT, cellStyles: { 'width': '30%' }, config: {
          disabled: this.isDisabled
        }
      },
      {
        key: 'type', label: 'Type', type: TABLE_COLUMNS_TYPES.ROW_SELECT, options: this.datasetDictionaries, config: {
          validators: [this.isEmpty],
          disabled: this.isDisabled
        }
      },
      {
        key: 'values', label: 'Applicable Values', type: TABLE_COLUMNS_TYPES.CHIPS, config: {
          disabled: this.isDisabled,
          isHierarchy: (row) => {
            return row.hierarchy ? true : false;
          }
        }
      },
      {
        key: 'manage-app-values',
        label: '',
        type: TABLE_COLUMNS_TYPES.ACTION,
        icon: 'sort',
        onClick: (element) => {
          if (!this.readOnly) { this.openHierarchyModal(element); }
        },
        config: {
          disabled: this.isDisabled,
          isRender: (row) => {
            return !this.isDisabled(row);
          }
        },
      },
      {
        key: 'delete',
        label: '',
        type: TABLE_COLUMNS_TYPES.ACTION,
        icon: 'delete',
        onClick: (element) => {
          if (!this.readOnly) { this.deleteRow(element); }
        },
        config: {
          disabled: this.isDisabled,
          isRender: (row) => {
            return !this.isDisabled(row);
          }
        },
      }
    ];
  }

  isNotUnique = (row, column) => {
    if (this.isDisabled(row)) {
      return null;
    }
    const value = row[column.key];

    const duplicate = this.data.find((dataRow: TableRow) => {
      return row !== dataRow && dataRow[column.key] === value;
    });

    return duplicate ? 'this field should be unique' : null;
  }

  isEmpty = (row, column) => {
    if (this.isDisabled(row)) {
      return null;
    }
    const value = row[column.key];

    return Boolean(value) ? null : '*required';
  }

  isDisabled = (row) => {
    return this.disableData.includes(row);
  }

  ngOnInit() {

    this.authenticationService.readOnly.pipe(first()).subscribe(res => {
      this.readOnly = res;
    });

    this.datasetDictionaries = this.dictionaryService.getDictionary('ReportDataSetDefinition.col_dtypes');
    this.columns = this.getColumns();
    if (!this.reportDatasetsService.isNewRDS) {
      this.getReportDatasetDetails();
    }

    this.reportDatasetsService.$reportDatasetDetails.subscribe((reportDatasetDetails: ReportDataset) => {
      if (reportDatasetDetails) {
        if (reportDatasetDetails['data_in_db_dt'] && reportDatasetDetails['data_in_db_dt'] !== 'NaT') {
          this.reportDatasetsService.data_in_db = true;
        } else { this.reportDatasetsService.data_in_db = false; }
        this.data = Object.assign(reportDatasetDetails.columns);
        this.disableData = this.data.filter((item: any) => {
          return DISABLED_ROWS_IDS.includes(item.id);
        });
        this.loading = false;
      }
    });

    this.reportDatasetsService.loadingStatus
      .subscribe(loadingStatus => {
        this.loading = !isResolved(loadingStatus);
      });

    this.duplicatedReport = this.reportDatasetsService.reportDatasetDetails;
    if ((!this.duplicatedReport) || (!this.duplicatedReport.hasOwnProperty('newName'))) {
      this.show = false;
      this.showOriginRdsName = true;
      this.duplicatedReport = null;
    } else {
      this.show = true;
      this.showOriginRdsName = false;
    }
  }

  ngAfterViewChecked() {

  }

  ngOnDestroy() {

    this.show = false;
    this.showOriginRdsName = true;
    if (this.tableDataObjChanged) {
      this.onSave();
    }
  }

  getReportDatasetDetails() {
    this.reportDatasetsService.getReportDatasetDetails(this.name).subscribe();
  }

  downloadStructure() {
    this.reportDatasetsService.downloadRDSStructure(this.name).subscribe(data => saveAs(data, this.name + '.csv'));
  }

  uploadStructure() {
    const dialogRef = this.dialog.open(UploadDatasetStructureComponent, {
      width: '700px',
      data: {
        datasetInfo: {
          name: this.route.snapshot.params.reportName
        },
      }
    });

    dialogRef.afterClosed().subscribe(result => {
      if (result) {
        const file = result['file'];
        const delimiter = result['delimiter'];
        const reader: FileReader = new FileReader();
        reader.readAsText(file);
        reader.onload = (e) => {
          const csv: string = reader.result as string;
          const lines = csv.split('\n');
          const array = [];
          for (let i = 1; i < lines.length; i++) {
            let arr = [];
            let line = lines[i].trim();
            let lastIndex = null;
            if (line !== '') {
              if (delimiter === ',') {
                if (line[line.length - 1] === ']') {
                  for (let j = line.length - 2; j > 0; j--) {
                    if (line[j] === '[') {
                      lastIndex = j;
                      line = line.slice(0, lastIndex) + '"' + line.slice(lastIndex) + '"';
                      break;
                    }
                  }
                }
                if (line[line.length - 1] === '"') {
                  for (let k = line.length - 2; k > 0; k--) {
                    if (line[k] === '"') {
                      lastIndex = k;
                      arr[3] = line.substring(lastIndex + 1, line.length - 1);
                      arr[3] = JSON.parse(arr[3].replace(/'/g, '"'));
                      line = line.substring(0, lastIndex - 1);
                      break;
                    }
                  }
                } else {
                  arr[3] = '';
                  line = line.substring(0, line.length - 1);
                }
                for (let l = line.length - 1; l > 0; l--) {
                  if (line[l] === ',') {
                    lastIndex = l;
                    arr[2] = line.substring(lastIndex + 1, line.length);
                    line = line.substring(0, lastIndex);
                    break;
                  }
                }

                lastIndex = line.indexOf(',');
                arr[0] = line.substring(0, lastIndex);
                arr[1] = line.substring(lastIndex + 1, line.length);
                if (arr[1].indexOf(',') > -1) {
                  arr[1] = arr[1].substring(1, arr[1].length - 1);
                }
              } else {
                arr = lines[i].split('|');
                arr[3] = arr[3].trim();
                if (arr[3] !== '') {
                  arr[3] = JSON.parse(arr[3].replace(/'/g, '"'));
                }
              }
              const obj = {
                id: arr[0],
                desc: arr[1],
                type: arr[2],
                values: arr[3]
              };
              array.push(obj);
            }
          }
          this.data = array;
          this.disableData = this.data.filter((item: any) => {
            return DISABLED_ROWS_IDS.includes(item.id);
          });
          this.setDataChanged();
        };
      }
    });

  }

  deleteRow(element) {
    this.dialog.open(DeleteConfirmationComponent).afterClosed().subscribe((isConfirmed: boolean) => {
      if (isConfirmed) {
        this.reportDatasetsService.saveReportDatasetDetails(this.data.filter((row) => row !== element)).subscribe(res => {
          if (!(res instanceof HttpErrorResponse)) {
            this.data = this.data.filter((row) => row !== element);
            this.resetBaseData();
            this.openSnackBar();
          }
        });
      }
    });
  }

  rowChange(e) {
    const isNotEmpty = disableButton(this.data, 'id');
    const dublicated = checkDuplicateInObject('id', this.data);
    if (isNotEmpty || dublicated) {
      this.inputsValid = true;
    } else {
      this.inputsValid = false;
    }
    this.setDataChanged();
  }

  openSnackBar() {
    const snackBarRef = this.snackBar.open('Report Dataset has been successfully saved', 'go back to RDS list', {
      duration: 2000
    });
    snackBarRef.onAction().subscribe(() => {
      this.router.navigate(['designer/report-datasets/']);
    });
  }

  goBack() {
    this.reportDatasetsService.isNewRDS = false;
    this.router.navigate(['designer/report-datasets/']);
  }

  onSave() {
    this.reportDatasetsService.saveReportDatasetDetails(this.data).subscribe(res => {
      if (!(res instanceof HttpErrorResponse)) {
        this.resetBaseData();
        this.openSnackBar();
      }
    });
  }

  openUploadModal() {
    this.dialog.open(UploadDatasetComponent, {
      width: '400px',
      data: {
        datasetInfo: {
          name: this.route.snapshot.params.reportName
        },
        isReportDataset: true
      }
    });
  }

  downloadFile() {
    this.reportDatasetsService.checkStatusCsvFile(this.name).subscribe((res) => {
      if (!(res instanceof HttpErrorResponse)) {
        this.reportDatasetsService.downloadRDScsvData(this.name).subscribe(data => saveAs(data, this.name + '.csv'));
      }
    });
  }

  setDataChanged() {
    this.dataChanged = !isEqual(this.data, this.baseData);
  }

  resetBaseData() {
    this.baseData = cloneDeep(this.data);
    this.setDataChanged();
  }

  iconContent(e) {
    if (e.fromElement === null) {
    } else if (e.fromElement.textContent === 'delete') {
      this.datasetsService.toolTipContent = 'Delete';
    } else if (e.fromElement.textContent === 'sort') {
      this.datasetsService.toolTipContent = 'Link/unlink hierarchy';
    }
  }

  openHierarchyModal(el) {

    const dialogRef = this.dialog.open(ManageApplicableValuesComponent, {
      width: '90%',
      data: {
        element: el,
        rdsName: this.route.snapshot.params.reportName,
        columnName: el.id,
        fromAction: 'rdsColumn'
      }
    });
    dialogRef.afterClosed().subscribe(result => {

      if (result === undefined) {
        // User clicked cancel or focus out from the modal

      } else if (result.action === 'save') {
        for (let i = 0; i < this.data.length; i++) {
          if (this.data[i].hierarchy === result.name) {
            const applicableValues = result.rowData.map(a => a.name);
            this.data[i].values = applicableValues;
          }
        }
        this.tableDataObjChanged = true;
        this.setDataChanged();

      } else if (result.action === 'unPairHierarchy') {
        this.data.find((o, i) => {
          if (o.id === el.id) {
            delete this.data[i].hierarchy;
            return true; // stop searching
          }
        });
        this.snackBar.open('Hierarchy un linked from column', null, { duration: 1000 });
        this.tableDataObjChanged = true;

      }

    });

  }

}

spec.ts file:

fdescribe('ReportDatasetCreationComponent', () => {
  let component: ReportDatasetCreationComponent;
  let fixture: ComponentFixture<ReportDatasetCreationComponent>;
  let service;
  let mySpy;
  let httpMock;
  let de: DebugElement;

  let mockData = {
    columns: [
      {
        desc: 'Unique identifier of event. (Mandatory)',
        id: 'id',
        type: 'str'
      },
      {
        id: 'CURRENCY',
        type: 'str'
      },
      {
        id: 'NAME',
        type: 'str'
      },
      {
        hierarchy: 'coockie',
        id: 'COUNTRY',
        type: 'str',
        values: [
          'a',
          'b',
          'e',
          '123456789123456789123456789123456789123456789123456789123456789123456789'
        ]
      },
      {
        hierarchy: 'q',
        id: 'PRICE',
        type: 'nbr',
        values: [
          'q',
          '1',
          '2'
        ]
      }
    ],
    data_in_db_dt: '2020-01-06T23:21:46.375819',
    desc: '',
    filter_criteria: {},
    name: 'motoDealRds',
    updt_by: 'Shubby dubby',
    updt_on: '2020-01-06T23:21:46.376801'
  };

  let mockMusicShop = {
    columns: [
      {
        desc: 'Unique identifier of event. (Mandatory)',
        id: 'id',
        type: 'str'
      },
      {
        id: 'product',
        type: 'str'
      },
      {
        id: 'price',
        type: 'nbr'
      },
      {
        id: 'currency',
        type: 'str'
      },
      {
        id: 'manufacturer',
        type: 'str'
      }
    ],
    data_in_db_dt: '2020-02-12T19:03:16.755805',
    desc: 'RDS for unit testings',
    filter_criteria: {},
    name: 'music_shop',
    updt_by: 'Nikhil',
    updt_on: '2020-02-12T19:03:16.755805'
  }

  let mockColumnElement = {
    hierarchy: 'company_hierarchy_mock',
    id: 'PRICE',
    type: 'nbr',
    values: ['q', '1', '2']
  };

  let mockHierarchy = {
    name: 'mock hierarchy',
    rowData: [
      { name: 'a', path: ['a'], type: 'folder', id: 1 },
      { name: 'b', path: ['a', 'b'], type: 'folder', id: 2 },
      { name: 'c', path: ['a', 'c'], type: 'folder', id: 3 },
      { name: 'd', path: ['a', 'd'], type: 'folder', id: 4 }
    ]
  };

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [DesignerModule, RouterTestingModule, HttpClientTestingModule],
      providers: [
        AuthenticationService,
        DictionaryService,
        MockBackend,
        BaseRequestOptions,
        {
          provide: Http,
          useFactory: (mockBackend: MockBackend, defaultOptions: RequestOptions) => {
            return new Http(mockBackend, defaultOptions);
          },
          deps: [MockBackend, BaseRequestOptions]
        }
      ]
    })
      .compileComponents();
  }));

  beforeEach(() => {
    // ReportDatasetCreationComponent.prototype.ngOnInit = () => { };

    fixture = TestBed.createComponent(ReportDatasetCreationComponent);
    component = fixture.componentInstance;
    component.dictionaryService = new DictionaryServiceMock(TestBed.get(HttpClient));
    service = TestBed.get(ReportDatasetsService);
    httpMock = TestBed.get(HttpTestingController);
    de = fixture.debugElement;
    component.name = 'motoDealRds';
    mySpy = spyOn(service, 'getReportDatasetDetails').and.callThrough();
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();

  });

  it('should spyOn getReportDatasetDetails and return fake data', () => {

    service.$reportDatasetDetails.next(mockMusicShop);
    // this function should be called in ngOnInit once
    expect(service.getReportDatasetDetails).toHaveBeenCalledTimes(1);

    // Should enter the if condition after the response
    expect(component.reportDatasetDetailsResponse).toEqual(jasmine.objectContaining({
      data_in_db_dt: '2020-02-12T19:03:16.755805'
    }));

    // Since the response contain data, the data_in_db should be true:
    // expect(service.data_in_db).toBeTruthy();

  });

  it('Should return different results since the mockData will be change', () => {

    // Now, let's try to changed the data:
    service.$reportDatasetDetails.next(undefined);

    // this function should be called in ngOnInit once
    expect(service.getReportDatasetDetails).toHaveBeenCalledTimes(1);

    // The if condition should not pass and should not pass and go to the else scope:
    expect(service.data_in_db).toBeFalsy();

  });

  // mock a duplicate report and test the if conditions and the variables inside
  it('Should test the if conditions and the variables inside', () => {
    component.duplicatedReport = mockMusicShop;
    expect(component.show).toBeFalsy();
    expect(component.showOriginRdsName).toBeTruthy();
    expect(component.show).toBeFalsy();

  });

  it('Should call ngOnDestroy and test the state of the variables', () => {
    component.ngOnDestroy();
    expect(component.show).toBeFalsy();
    expect(component.showOriginRdsName).toBeTruthy();
    expect(component.tableDataObjChanged).toBeFalsy();

  });

  it('Should call ngOnDestroy and test the state of the variables, but this time, expect to call onSave once', () => {
    service.reportDatasetDetails = mockMusicShop;
    component.tableDataObjChanged = true;
    const onSaveSpy = spyOn(component, 'onSave').and.callThrough();
    component.ngOnDestroy();
    expect(onSaveSpy).toHaveBeenCalledTimes(1);

  });

  it('Should call onSave with mock data', () => {

    service.$reportDatasetDetails.next(mockMusicShop);
    service.reportDatasetDetails = mockMusicShop;
    const onSaveSpy = spyOn(component, 'onSave').and.callThrough();
    const saveReportDatasetDetailsSpy = spyOn(service, 'saveReportDatasetDetails').and.returnValue(of(null));
    const resetBaseDataSpy = spyOn(component, 'resetBaseData').and.callThrough();
    const openSnackBarSpy = spyOn(component, 'openSnackBar').and.callThrough();
    component.onSave();
    expect(onSaveSpy).toHaveBeenCalledTimes(1);
    expect(saveReportDatasetDetailsSpy).toHaveBeenCalledTimes(1);

    // Since the response is not en error object, it should call resetBaseData and openSnackBar

    expect(resetBaseDataSpy).toHaveBeenCalledTimes(1);
    expect(openSnackBarSpy).toHaveBeenCalledTimes(1);

  });

  it('Should call onSave with mock data, but this time the response will not pass the if condition', () => {

    service.$reportDatasetDetails.next(mockMusicShop);
    service.reportDatasetDetails = mockMusicShop;

    const onSaveSpy = spyOn(component, 'onSave').and.callThrough();
    const saveReportDatasetDetailsSpy = spyOn(service, 'saveReportDatasetDetails').and.callThrough();
    const resetBaseDataSpy = spyOn(component, 'resetBaseData').and.callThrough();
    const openSnackBarSpy = spyOn(component, 'openSnackBar').and.callThrough();

    component.onSave();

    expect(onSaveSpy).toHaveBeenCalledTimes(1);
    expect(saveReportDatasetDetailsSpy).toHaveBeenCalledTimes(1);

    // Since the response is undefined, it should not call resetBaseData and openSnackBar

    expect(resetBaseDataSpy).toHaveBeenCalledTimes(0);
    expect(openSnackBarSpy).toHaveBeenCalledTimes(0);

  });

});


来源: stackexchange

问:
How to change state value in const mode?
I am a beginner in React-Native.
I tried to change the state value.
But it seems different with "class" mode. Now I am using "const" mode.
In const mode, how can I change the state value?</p>

```<code>import React from "react";
import {
   View,
   Text,
   Image,
   TouchableOpacity
} from "react-native";

const Product = props => {
    this.state = {
        checkIconURL : require('../../assets/ic_check_circle.png')
    }
    set = () => {
        this.setState({ checkIconURL : require('../../assets/empty.png') })  //But this lines gives err.
    }

    return (
        <View>
            <Text style={{ fontSize: 16}}>Everyday</Text>
            <TouchableOpacity onPress = {this.set}>
                <Image source = {this.state.checkIconURL} style={{width: 30, height: 30}} />
            </TouchableOpacity>
        </View>
    )
}
</code>```

答:
the following code is incorrect as you need to use Hook in a functional component to manage state.
React provides the useState hook to manage state in a functional component.</p>

```<code>import React ,{ useState } from "react";
import {
    View,
    Text,
    Image,
    TouchableOpacity
} from "react-native";

const Product = props => {
   const initialState = require('../../assets/ic_check_circle.png');
   const [ state,setState ]  = useState({checkIconURL: initialState});
   set = () => {
       const emptyImageUrl = require('../../assets/empty.png') });
           setState({...state, checkIconURL: emptyImageUrl })
       }

   return (
       <View>
           <Text style={{ fontSize: 16}}>Everyday</Text>
               <TouchableOpacity onPress = {this.set}>
                   <Image source = {this.state.checkIconURL} style={{width:
                       30, height: 30}} />
               </TouchableOpacity>
       </View>
    )
}
</code>```

ContentLicense: CC BY-SA 4.0

Body:
I am a beginner in React-Native.
I tried to change the state value.
But it seems different with "class" mode. Now I am using "const" mode.
In const mode, how can I change the state value?

import React from "react";
import {
   View,
   Text,
   Image,
   TouchableOpacity
} from "react-native";

const Product = props => {
    this.state = {
        checkIconURL : require('../../assets/ic_check_circle.png')
    }
    set = () => {
        this.setState({ checkIconURL : require('../../assets/empty.png') })  //But this lines gives err.
    }

    return (
        <View>
            <Text style={{ fontSize: 16}}>Everyday</Text>
            <TouchableOpacity onPress = {this.set}>
                <Image source = {this.state.checkIconURL} style={{width: 30, height: 30}} />
            </TouchableOpacity>
        </View>
    )
}

Body:
the following code is incorrect as you need to use Hook in a functional component to manage state.
React provides the useState hook to manage state in a functional component.

import React ,{ useState } from "react";
import {
    View,
    Text,
    Image,
    TouchableOpacity
} from "react-native";

const Product = props => {
   const initialState = require('../../assets/ic_check_circle.png');
   const [ state,setState ]  = useState({checkIconURL: initialState});
   set = () => {
       const emptyImageUrl = require('../../assets/empty.png') });
           setState({...state, checkIconURL: emptyImageUrl })
       }

   return (
       <View>
           <Text style={{ fontSize: 16}}>Everyday</Text>
               <TouchableOpacity onPress = {this.set}>
                   <Image source = {this.state.checkIconURL} style={{width:
                       30, height: 30}} />
               </TouchableOpacity>
       </View>
    )
}


问:
Get total [checkbox] value
HTML:

```<code><input type="checkbox" name="product[book]" value="10" /> book
<input type="checkbox" name="product[plane]" value="20" /> plane
</code>```

PHP:

```
<code>
foreach ($_POST['product'] as $name => $value) {
    echo $value;
}
</code>
```

How to get the total (sum) value if the user select two fields (book & plane)

答:
You can use array_sum:

```<code>$sum = array_sum(array_map('intval', $_POST['product']));
</code>```

Assuming you already checked validity of the $_POST['product'] field.

ContentLicense: CC BY-SA 3.0

Title: Get total [checkbox] value

Body:
HTML:

<input type="checkbox" name="product[book]" value="10" /> book
<input type="checkbox" name="product[plane]" value="20" /> plane

PHP:

foreach ($_POST['product'] as $name => $value) {
    echo $value;
}

How to get the total (sum) value if the user select two fields (book & plane)

Body:
You can use array_sum:

$sum = array_sum(array_map('intval', $_POST['product']));

Assuming you already checked validity of the $_POST['product'] field.


问:
Is it possible to print out something in the browser debug console, so that when clicked on, will invoke a function?
While debugging, I might have an element el, and I can print it out using console.log(el).

There can be 50 or even 100 of them, while debugging.  Can I print out something in the debug console in Google Chrome, so that when I click on it, will make the element come into focus in the webpage?

I tried:

```<code>    let tmpLink = document.createElement("a");
    tmpLink.href = "#";   // now it is a link instead of anchor
    tmpLink.innerHTML = "Click Me";
    tmpLink.addEventListener("click", function() {
      el.focus();
    });
    console.log(tmpLink);
</code>```

and the link is printed out in the debug console, except when I click on it, doesn't bring me to the element on page. Is there a way to do it?

One less desirable way is to do

```<code>window.focusArr = window.focusArr || [];
window.focusArr.push(el);
console.log(`Element ${window.focusArr.length - 1}`);
</code>```

so I can just do in the console:

```<code>focusArr[21].focus()
</code>```

But is there a way to make clicking on something work in the debug console?

答:
This isn't exactly what you're asking, but it may achieve the functionality you're looking for while debugging. In Chrome, if you log the actual element that you'd want to focus on, you can log an exact reference to the element, and then right click on it in the console and select "Scroll into view":

(For your code, you'd have to do console.log(el) so that the el that you're interested in can be right-clicked on and zoomed to)

Body:
While debugging, I might have an element el, and I can print it out using console.log(el).

There can be 50 or even 100 of them, while debugging.  Can I print out something in the debug console in Google Chrome, so that when I click on it, will make the element come into focus in the webpage?

I tried:

    let tmpLink = document.createElement("a");
    tmpLink.href = "#";   // now it is a link instead of anchor
    tmpLink.innerHTML = "Click Me";
    tmpLink.addEventListener("click", function() {
      el.focus();
    });
    console.log(tmpLink);

and the link is printed out in the debug console, except when I click on it, doesn't bring me to the element on page. Is there a way to do it?

One less desirable way is to do

window.focusArr = window.focusArr || [];
window.focusArr.push(el);
console.log(`Element ${window.focusArr.length - 1}`);

so I can just do in the console:

focusArr[21].focus()

But is there a way to make clicking on something work in the debug console?


问:
What is the best way to `sort_by` a huge file that doesn't fit in memory with `jq`
I'm working with a text file that has one JSON object per line and I want to use jq to select, group_by (key1), and sort_by (key1) the file. The file looks like this:

```<code># /tmp/sample.json
{"key1": "value11", "key2": "value21", "key3": "value31"}
{"key1": "value11", "key2": "value22", "key3": "value32"}
{"key1": "value11", "key2": "value22", "key3": "value32"}
{"key1": "value13", "key2": "value23", "key3": "value33"}
{"key1": "value13", "key2": "value24", "key3": "value34"}
{"key1": "value16", "key2": "value26", "key3": "value36"}
{"key1": "value17", "key2": "value27", "key3": "value37"}
...
</code>```

I'm running the file through Hadoop MapReduce in a similar manner to this question:

```<code>hadoop jar $HADOOP_HOME/hadoop-streaming.jar \
    -files $HOME/bin/jq,$HOME/proj-map.jq,$HOME/proj-reduce.jq \
    -mapper "./jq -c --from-file=proj-map.jq" \
    -reducer "./jq -ncr --from-file=proj-reduce.jq" \
    -input  /tmp/sample.json \
    -output /tmp/sample.json.output
</code>```

with

```<code>#proj-map.jq
# some transformation
{key1, key2}
</code>```

and

```<code>#proj-reduce.jq
# by @peak -- https://stackoverflow.com/a/45715729/948914
# sort-free stream-oriented variant of group_by/1
# f should always evaluate to a string.
# Output: a stream of arrays, one array per group
def GROUPS_BY(stream; f): reduce stream as $x ({}; .[$x|f] += [$x] ) | .[] ;

GROUPS_BY(inputs|.key1; .) | {key1: .[0], size: length} | (.size|tostring) + "\t" + tostring
</code>```

The above yields something that I can feed into Unix sort for sorting:

```<code>3 {"key1": "value11", "size": 3}
2 {"key1": "value13", "size": 2}
1 {"key1": "value16", "size": 1}
1 {"key1": "value17", "size": 1}
</code>```

This works. Now, I don't want to rely on Unix sort and I'm looking for a way to use jq's sort_by(). I figured out that this can be challenging because from what I understand, sort_by() requires an array as input, which implies that the array is loaded in memory. Since the file may not fit in memory, I'm looking for a way using jq's sort_by() without reading the entire file in memory. In particular, I'm interested in an efficient, streaming-type way of sorting, similar to Unix sort, or to the streaming group_by().

If there is no such way, then to the best of my knowledge is this answer, which combines jq and Unix sort, as I showed above. Obviously it would be great is sort_by worked like Unix sort but I don't have the means to find out.

答:
[The following was written before the question was updated to explain that the input consists of multiple JSON entities.]

To simplify things a bit, the following assumes that you have a huge file consisting of a single JSON array.  Since, by assumption, this file is too big to read into memory, the first step will be to get each of the top-level array elements on a line by itself.  That can be done using jq's --stream command-line option, as described in the jq FAQ, e.g. perhaps along the lines of:

```<code>jq -cn --stream 'fromstream( inputs|(.[0] |= .[1:]) | select(. != [[]]) )'
</code>```

The next step is to prefix each of these lines with the "sort by" value, as described in the link included in the Q.  (That is, jq can easily be used.)

Next, run the operating system sort.

Finally, if you really need the result as a single large array, you could use a text-processing tool (e.g. awk).

Body:
I'm working with a text file that has one JSON object per line and I want to use jq to select, group_by (key1), and sort_by (key1) the file. The file looks like this:

# /tmp/sample.json
{"key1": "value11", "key2": "value21", "key3": "value31"}
{"key1": "value11", "key2": "value22", "key3": "value32"}
{"key1": "value11", "key2": "value22", "key3": "value32"}
{"key1": "value13", "key2": "value23", "key3": "value33"}
{"key1": "value13", "key2": "value24", "key3": "value34"}
{"key1": "value16", "key2": "value26", "key3": "value36"}
{"key1": "value17", "key2": "value27", "key3": "value37"}
...

I'm running the file through Hadoop MapReduce in a similar manner to this question:

hadoop jar $HADOOP_HOME/hadoop-streaming.jar \
    -files $HOME/bin/jq,$HOME/proj-map.jq,$HOME/proj-reduce.jq \
    -mapper "./jq -c --from-file=proj-map.jq" \
    -reducer "./jq -ncr --from-file=proj-reduce.jq" \
    -input  /tmp/sample.json \
    -output /tmp/sample.json.output

with

#proj-map.jq
# some transformation
{key1, key2}

and

#proj-reduce.jq
# by @peak -- https://stackoverflow.com/a/45715729/948914
# sort-free stream-oriented variant of group_by/1
# f should always evaluate to a string.
# Output: a stream of arrays, one array per group
def GROUPS_BY(stream; f): reduce stream as $x ({}; .[$x|f] += [$x] ) | .[] ;

GROUPS_BY(inputs|.key1; .) | {key1: .[0], size: length} | (.size|tostring) + "\t" + tostring

The above yields something that I can feed into Unix sort for sorting:

3 {"key1": "value11", "size": 3}
2 {"key1": "value13", "size": 2}
1 {"key1": "value16", "size": 1}
1 {"key1": "value17", "size": 1}

This works. Now, I don't want to rely on Unix sort and I'm looking for a way to use jq's sort_by(). I figured out that this can be challenging because from what I understand, sort_by() requires an array as input, which implies that the array is loaded in memory. Since the file may not fit in memory, I'm looking for a way using jq's sort_by() without reading the entire file in memory. In particular, I'm interested in an efficient, streaming-type way of sorting, similar to Unix sort, or to the streaming group_by().

If there is no such way, then to the best of my knowledge is this answer, which combines jq and Unix sort, as I showed above. Obviously it would be great is sort_by worked like Unix sort but I don't have the means to find out.

Body:
[The following was written before the question was updated to explain that the input consists of multiple JSON entities.]

To simplify things a bit, the following assumes that you have a huge file consisting of a single JSON array.  Since, by assumption, this file is too big to read into memory, the first step will be to get each of the top-level array elements on a line by itself.  That can be done using jq's --stream command-line option, as described in the jq FAQ, e.g. perhaps along the lines of:

jq -cn --stream 'fromstream( inputs|(.[0] |= .[1:]) | select(. != [[]]) )'

The next step is to prefix each of these lines with the "sort by" value, as described in the link included in the Q.  (That is, jq can easily be used.)

Next, run the operating system sort.

Finally, if you really need the result as a single large array, you could use a text-processing tool (e.g. awk).


问:
Save String from more than 1 TextField in UserDefaults with SwiftUI
I can't figure out how to save the userData for more than 1 Textfield.

I managed that the data gets saved for 1 Textfield but if i "duplicate" the code and the Textfield, it only saves one textfield userData...

My userData file:

```<code>import SwiftUI
import Combine

class UserData : ObservableObject {

 private static let userDefaultBuyingPrice = "BuyingPrice"
 private static let userDefaultRent = "Rent"

 @Published var BuyingPrice = UserDefaults.standard.string(forKey: UserData.userDefaultBuyingPrice) ?? ""

  @Published var Rent = UserDefaults.standard.string(forKey: UserData.userDefaultRent) ?? ""
  private var canc: AnyCancellable!

 }
</code>```

My ContentView File:

```<code>struct ContentView: View {

@ObservedObject var userData = UserData()

var body: some View {
    VStack{
        TextField("BuyingPrice", text: $userData.BuyingPrice)
            .font(.title)
            .keyboardType(.decimalPad)

        TextField("Rent", text: $userData.Rent)
            .font(.title)
            .keyboardType(.decimalPad)
    }
}
</code>```

}

Only the second value is saved, cannot figure out why the second one is not working

If there is a easier solution for the whole userData saving i would be grateful for the input.

THanks,

答:
UserData:

```<code>import SwiftUI
import Combine

class UserData : ObservableObject {

  private static let userDefaultBuyingPrice = "BuyingPrice"
  private static let userDefaultRent = "Rent"

  @Published var BuyingPrice = UserDefaults.standard.string(forKey: UserData.userDefaultBuyingPrice) ?? "" {
    didSet {
      UserDefaults.standard.set(self.BuyingPrice, forKey: UserData.userDefaultBuyingPrice)
    }
  }

  @Published var Rent = UserDefaults.standard.string(forKey: UserData.userDefaultRent) ?? "" {
    didSet {
      UserDefaults.standard.set(self.Rent, forKey: UserData.userDefaultRent)
    }
  }
  private var canc: AnyCancellable!

}
</code>```

ContentView:

```<code>import SwiftUI

struct ContentView: View {
  @EnvironmentObject var userData: UserData

   var body: some View {
       VStack{
           TextField("BuyingPrice", text: $userData.BuyingPrice)
               .font(.title)
               .keyboardType(.decimalPad)

           TextField("Rent", text: $userData.Rent)
               .font(.title)
               .keyboardType(.decimalPad)
       }
   }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
      ContentView().environmentObject(UserData())
    }
}
</code>```

In SceneDelegate.swift:

```<code>class SceneDelegate: UIResponder, UIWindowSceneDelegate {

  var window: UIWindow?
  var userData = UserData() //add this line

//then modify this  line:
window.rootViewController = UIHostingController(rootView: contentView)
//to this:
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(userData))
</code>```

Body:
I can't figure out how to save the userData for more than 1 Textfield.

I managed that the data gets saved for 1 Textfield but if i "duplicate" the code and the Textfield, it only saves one textfield userData...

My userData file:

import SwiftUI
import Combine

class UserData : ObservableObject {

 private static let userDefaultBuyingPrice = "BuyingPrice"
 private static let userDefaultRent = "Rent"

 @Published var BuyingPrice = UserDefaults.standard.string(forKey: UserData.userDefaultBuyingPrice) ?? ""

  @Published var Rent = UserDefaults.standard.string(forKey: UserData.userDefaultRent) ?? ""
  private var canc: AnyCancellable!

 }

My ContentView File:

struct ContentView: View {

@ObservedObject var userData = UserData()

var body: some View {
    VStack{
        TextField("BuyingPrice", text: $userData.BuyingPrice)
            .font(.title)
            .keyboardType(.decimalPad)

        TextField("Rent", text: $userData.Rent)
            .font(.title)
            .keyboardType(.decimalPad)
    }
}

}

Only the second value is saved, cannot figure out why the second one is not working

If there is a easier solution for the whole userData saving i would be grateful for the input.

THanks,

Body:
UserData:

import SwiftUI
import Combine

class UserData : ObservableObject {

  private static let userDefaultBuyingPrice = "BuyingPrice"
  private static let userDefaultRent = "Rent"

  @Published var BuyingPrice = UserDefaults.standard.string(forKey: UserData.userDefaultBuyingPrice) ?? "" {
    didSet {
      UserDefaults.standard.set(self.BuyingPrice, forKey: UserData.userDefaultBuyingPrice)
    }
  }

  @Published var Rent = UserDefaults.standard.string(forKey: UserData.userDefaultRent) ?? "" {
    didSet {
      UserDefaults.standard.set(self.Rent, forKey: UserData.userDefaultRent)
    }
  }
  private var canc: AnyCancellable!

}

ContentView:

import SwiftUI

struct ContentView: View {
  @EnvironmentObject var userData: UserData

   var body: some View {
       VStack{
           TextField("BuyingPrice", text: $userData.BuyingPrice)
               .font(.title)
               .keyboardType(.decimalPad)

           TextField("Rent", text: $userData.Rent)
               .font(.title)
               .keyboardType(.decimalPad)
       }
   }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
      ContentView().environmentObject(UserData())
    }
}

In SceneDelegate.swift:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

  var window: UIWindow?
  var userData = UserData() //add this line

//then modify this  line:
window.rootViewController = UIHostingController(rootView: contentView)
//to this:
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(userData))


问:
Global error handling of file_get_contents() PHP
I am occasionally getting errors while using file_get_contents and its used a fair bit in my script. I know I can suppress errors individually with @file_get_contents and that I can set global error messages with

```<code>//error handler function
function customError($errno)
  {
  echo 'Oh No!';
  }

//set error handler
set_error_handler("customError");
</code>```

But how do I specifically set an error handler for all file_get_content's uses?

Thanks

答:
your error handler function would need tobe more comprehensive then that.
you would do something like:</p>

```<code><?php
function customError($errno,$errstr){

    switch ($errno) {
        case E_USER_ERROR:
            echo "<b>ERROR</b> $errstr<br />\n";
            break;

        case E_USER_WARNING:
            echo "<b>WARNING</b> $errstr<br />\n";
            break;

        case E_USER_NOTICE:
            echo "<b>NOTICE</b> $errstr<br />\n";
            break;

        default:
            echo "Whoops there was an error in the code, check below for more infomation:<br/>\n";
            break;
    }
    return true;
}

set_error_handler("customError");

$filename = 'somemissingfile.txt';
$file = file_get_contents($filename);
//add the trigger_error after your file_get_contents
if($file===false){trigger_error('Could not get:'.$filename.' - on line 27<br/>',E_USER_ERROR);}

?>
</code>```

Body:
I am occasionally getting errors while using file_get_contents and its used a fair bit in my script. I know I can suppress errors individually with @file_get_contents and that I can set global error messages with

//error handler function
function customError($errno)
  {
  echo 'Oh No!';
  }

//set error handler
set_error_handler("customError");

But how do I specifically set an error handler for all file_get_content's uses?

Thanks

Body:
your error handler function would need tobe more comprehensive then that.
you would do something like:

<?php
function customError($errno,$errstr){

    switch ($errno) {
        case E_USER_ERROR:
            echo "<b>ERROR</b> $errstr<br />\n";
            break;

        case E_USER_WARNING:
            echo "<b>WARNING</b> $errstr<br />\n";
            break;

        case E_USER_NOTICE:
            echo "<b>NOTICE</b> $errstr<br />\n";
            break;

        default:
            echo "Whoops there was an error in the code, check below for more infomation:<br/>\n";
            break;
    }
    return true;
}

set_error_handler("customError");

$filename = 'somemissingfile.txt';
$file = file_get_contents($filename);
//add the trigger_error after your file_get_contents
if($file===false){trigger_error('Could not get:'.$filename.' - on line 27<br/>',E_USER_ERROR);}

?>


问:
Traverse from Child Node to Ancestor Node using XPath in Selenium WebDriver
I am trying to traverse to the ancestor node from a child node using xpath, but it does not want to work.

```<code>WebElement menuItemText = driver.findElement(By.xpath("//span[contains( text(), '" + itemTitle + "')]"));
menuItemText = menuItemText.findElement(By.xpath(".//parent::*[@attribute='onclick']"));
menuItemText.click();
</code>```

The nodes is:

        Text!

```

However, the function name is very complex and each node has strange unique IDs. Not that it should matter too much but I want to traverse up the DOM from the child node to the parent / ancestor node that includes an onclick attribute. I want to get the first onclick when traversing up the DOM. So, the onclick=myFunction() is what I want to get.

Is that possible? I tried doing a while() loop and it worked for once but not anymore.

答:
You can directly access the ancestor element from child node by using ancestor keyword as follows:</p>

```<code>//span[contains(text(),\"Text\")]/ancestor::div[@onclick]
</code>```

Body:
I am trying to traverse to the ancestor node from a child node using xpath, but it does not want to work.

WebElement menuItemText = driver.findElement(By.xpath("//span[contains( text(), '" + itemTitle + "')]"));
menuItemText = menuItemText.findElement(By.xpath(".//parent::*[@attribute='onclick']"));
menuItemText.click();

The nodes is:

        Text!

However, the function name is very complex and each node has strange unique IDs. Not that it should matter too much but I want to traverse up the DOM from the child node to the parent / ancestor node that includes an onclick attribute. I want to get the first onclick when traversing up the DOM. So, the onclick=myFunction() is what I want to get.

Is that possible? I tried doing a while() loop and it worked for once but not anymore.

Body:
You can directly access the ancestor element from child node by using ancestor keyword as follows:

//span[contains(text(),\"Text\")]/ancestor::div[@onclick]


问:
How do I specify a path to a data file?
I am learning javascript and AJAX.  I am creating a webpage that should display dynamically changing data, and have the webpage refresh when the data in the data file changes.  I did some searching around for an approach and found something that I can use.  It uses two javscript functions ReloadData and processReqChange (see below).  These functions reside in a file named reloader.js.
The code will update the webpage when the data in the file liveData changes. The reloader.js file is located in the root of the webpage structure, e.g. same directory as index.html.
The file liveData also is contained in the directory.  if I manually change the data in liveData the data on the webpage will change.  The liveData file is accessed by the line:

```<code>url = 'liveData?' + now.getTime();
</code>```

The problem is I have a C program that generates a data file in /home/myUsername/liveData. I would like to have the reloader.js read the liveData file in /home/myUsername/.  If I change the url line to:

```<code>`url = '/home/myUsername/liveData?' + now.getTime();`
</code>```

I get an error stating that it can't find the liveData file.
how can I fix this?
Thanks
J

```<code>var req;
function reloadData()
{
   var now = new Date();
   url = 'liveData?' + now.getTime(); // file that contains data to be displayed
   try {
      req = new XMLHttpRequest();
   } catch (e) {
      try {
         req = new ActiveXObject("Msxml2.XMLHTTP");
      } catch (e) {
         try {
            req = new ActiveXObject("Microsoft.XMLHTTP");
         } catch (oc) {
            alert("No AJAX Support");
            return;
         }
      }
   }

   req.onreadystatechange = processReqChange;
   req.open("GET", url, true);
   req.send(null);
}

function processReqChange()
{
   // If req shows "complete"
   if (req.readyState == 4)
   {
      dataDiv = document.getElementById('currentData');

      // If "OK"
      if (req.status == 200)
      {
         // Set current data text
         dataDiv.innerHTML = req.responseText;

         // Start new timer (1 min)
         timeoutID = setTimeout('reloadData()', 1000);
         // changed 60000 (1 min) to 1000 (1 sec)
      }
      else
      {
         // Flag error
         dataDiv.innerHTML = '<p>There was a problem retrieving data: ' + req.statusText + '</p>';
      }
   }
}
</code>```

答:
Firstly, there's little need to use XHR these days, and certainly not with the old ActiveX fallbacks you have.  The Fetch API is well-supported.

Next, you will need some form of web server running to be able to serve that file data if you want it accessible via script.  You can't open an arbitrary path on the server from the client.  The server chooses what to data to send in response to a request.

url = '/home/myUsername/liveData?' + now.getTime()

I assume you're doing the getTime() thing on the querystring for cache busting.  Don't bother.  Use the correct Cache-Control headers instead.

Finally, if you can implement the appropriate server, throw all this away and use the EventSource API/Server-Sent Events.  This way, the server can notify you immediately when a change occurs, via a very simple API you can implement.

Title: How do I specify a path to a data file?

Body:
I am learning javascript and AJAX.  I am creating a webpage that should display dynamically changing data, and have the webpage refresh when the data in the data file changes.  I did some searching around for an approach and found something that I can use.  It uses two javscript functions ReloadData and processReqChange (see below).  These functions reside in a file named reloader.js.
The code will update the webpage when the data in the file liveData changes. The reloader.js file is located in the root of the webpage structure, e.g. same directory as index.html.
The file liveData also is contained in the directory.  if I manually change the data in liveData the data on the webpage will change.  The liveData file is accessed by the line:

url = 'liveData?' + now.getTime();

The problem is I have a C program that generates a data file in /home/myUsername/liveData. I would like to have the reloader.js read the liveData file in /home/myUsername/.  If I change the url line to:

`url = '/home/myUsername/liveData?' + now.getTime();`

I get an error stating that it can't find the liveData file.
how can I fix this?
Thanks
J

var req;
function reloadData()
{
   var now = new Date();
   url = 'liveData?' + now.getTime(); // file that contains data to be displayed
   try {
      req = new XMLHttpRequest();
   } catch (e) {
      try {
         req = new ActiveXObject("Msxml2.XMLHTTP");
      } catch (e) {
         try {
            req = new ActiveXObject("Microsoft.XMLHTTP");
         } catch (oc) {
            alert("No AJAX Support");
            return;
         }
      }
   }

   req.onreadystatechange = processReqChange;
   req.open("GET", url, true);
   req.send(null);
}

function processReqChange()
{
   // If req shows "complete"
   if (req.readyState == 4)
   {
      dataDiv = document.getElementById('currentData');

      // If "OK"
      if (req.status == 200)
      {
         // Set current data text
         dataDiv.innerHTML = req.responseText;

         // Start new timer (1 min)
         timeoutID = setTimeout('reloadData()', 1000);
         // changed 60000 (1 min) to 1000 (1 sec)
      }
      else
      {
         // Flag error
         dataDiv.innerHTML = '
There was a problem retrieving data: ' + req.statusText + '
';
      }
   }
}


问:
How to multiple each row of one matrix with another matrix in R?
For the following two matrix,

```<code>yy=matrix(c(1:40), nrow = 10, ncol = 8)
tt=diag(1:4)
</code>```

I would like to create a new matrix yy_new=matrix(NA, nrow = 10, ncol=ncol(tt)) by multiplying each row and first 4 column of yy by tt . Example, for the first row yy_new=yy[1,1:4]%*%tt, second row is
yy_new=yy[2,1:4]%*%tt. Finally I want the mean of yy_new at each column as yy_new=apply(yy_new,2,mean). The folowing loop is working well, but for large data set it is time comsuming.

```<code> yy_new=matrix(NA, nrow = 10, ncol=ncol(tt))
 for ( it in 1:10){
      for ( tim in 1:4){
        yy_new[it, tim]=yy[it,tim]*tt[tim,tim]
      }
    }
yy_new=apply(yy_new,2,mean)
</code>```

similarly, I want another matrix yy_new1 by considering the last four column of yy

```<code> yy_new1=matrix(NA, nrow = 10, ncol=ncol(tt))
</code>```

How can I do it efficiently using any built-in function or customized function?
Any help is appreciated.

答:
Here is a shorter (and faster) version for yy_new

```<code>yy_new <- rowMeans(apply(yy[, 1:4], 1, function(row) row %*% tt))
</code>```

Similarly for the last 4 columns of yy

```<code>yy_new1 <- rowMeans(apply(yy[, (ncol(yy)-3):ncol(yy)], 1, function(row) row %*% tt))
</code>```

Note that rowMeans and colMeans are generally faster than apply(..., 1, mean) and apply(..., 2, mean).

Here are results from a microbenchmark comparison

```<code>library(microbenchmark)
res <- microbenchmark(
    rowMeans_apply = {
        yy_new = rowMeans(apply(yy[, 1:4], 1, function(row) row %*% tt))
    },
    for_loop = {
        yy_new=matrix(NA, nrow = 10, ncol=ncol(tt))
         for ( it in 1:10){
              for ( tim in 1:4){
                yy_new[it, tim]=yy[it,tim]*tt[tim,tim]
              }
            }
    }
)
res
#Unit: microseconds
#           expr      min       lq      mean   median       uq       max neval
# rowMeans_apply   73.148   82.097  116.8959  101.329  123.863  1348.141   100
#       for_loop 3985.521 4141.633 5017.9808 4421.285 5020.425 18574.364   100
</code>```

Update

In response to your comment, you could do something like this:

```<code>f <- function(x) rowMeans(apply(x, 1, function(row) row %*% tt))
sapply(split.default(as.data.frame(yy), rep(1:2, each = 4)), f)
#         1     2
#[1,]   5.5   5.5
#[2,]  31.0  31.0
#[3,]  76.5  76.5
#[4,] 142.0 142.0
</code>```

Explanation: split.default here splits the data.frame into the first 4 and last 4 columns and stores them as two data.frames in a list; then we use sapply to loop through the list elements and calculate the required quantity as requested.  The resulting output object is a matrix.

Body:
For the following two matrix,

yy=matrix(c(1:40), nrow = 10, ncol = 8)
tt=diag(1:4)

I would like to create a new matrix yy_new=matrix(NA, nrow = 10, ncol=ncol(tt)) by multiplying each row and first 4 column of yy by tt . Example, for the first row yy_new=yy[1,1:4]%*%tt, second row is
yy_new=yy[2,1:4]%*%tt. Finally I want the mean of yy_new at each column as yy_new=apply(yy_new,2,mean). The folowing loop is working well, but for large data set it is time comsuming.

 yy_new=matrix(NA, nrow = 10, ncol=ncol(tt))
 for ( it in 1:10){
      for ( tim in 1:4){
        yy_new[it, tim]=yy[it,tim]*tt[tim,tim]
      }
    }
yy_new=apply(yy_new,2,mean)

similarly, I want another matrix yy_new1 by considering the last four column of yy

 yy_new1=matrix(NA, nrow = 10, ncol=ncol(tt))

How can I do it efficiently using any built-in function or customized function?
Any help is appreciated.

Body:
Here is a shorter (and faster) version for yy_new

yy_new <- rowMeans(apply(yy[, 1:4], 1, function(row) row %*% tt))

Similarly for the last 4 columns of yy

yy_new1 <- rowMeans(apply(yy[, (ncol(yy)-3):ncol(yy)], 1, function(row) row %*% tt))

Note that rowMeans and colMeans are generally faster than apply(..., 1, mean) and apply(..., 2, mean).

Here are results from a microbenchmark comparison

library(microbenchmark)
res <- microbenchmark(
    rowMeans_apply = {
        yy_new = rowMeans(apply(yy[, 1:4], 1, function(row) row %*% tt))
    },
    for_loop = {
        yy_new=matrix(NA, nrow = 10, ncol=ncol(tt))
         for ( it in 1:10){
              for ( tim in 1:4){
                yy_new[it, tim]=yy[it,tim]*tt[tim,tim]
              }
            }
    }
)
res
#Unit: microseconds
#           expr      min       lq      mean   median       uq       max neval
# rowMeans_apply   73.148   82.097  116.8959  101.329  123.863  1348.141   100
#       for_loop 3985.521 4141.633 5017.9808 4421.285 5020.425 18574.364   100

Update

In response to your comment, you could do something like this:

f <- function(x) rowMeans(apply(x, 1, function(row) row %*% tt))
sapply(split.default(as.data.frame(yy), rep(1:2, each = 4)), f)
#         1     2
#[1,]   5.5   5.5
#[2,]  31.0  31.0
#[3,]  76.5  76.5
#[4,] 142.0 142.0

Explanation: split.default here splits the data.frame into the first 4 and last 4 columns and stores them as two data.frames in a list; then we use sapply to loop through the list elements and calculate the required quantity as requested.  The resulting output object is a matrix.


问:
How can I avoid one word on the last line with CSS?
Suppose a text shows up as follows on an HTML page (just one word too long to fit on one line):

```<code>Lorem ipsum dolores amet foo
bar
</code>```

How can one avoid with CSS that the last word appears on the last line, and force two (or more)?

```<code>Lorem ipsum dolores amet
foo bar
</code>```

答:
here's some newbie php that you might use if you wanted to do this in wordpress</p>

```<code>function add_nobr_spans($string) {
  $words = explode(' ', $string);
  if (count($words) < 3){
    return $string;
  }
  $last_word = array_pop($words);
  while ($last_word == "") {
    $last_word = array_pop($words);
  }
  if (preg_match('#[<>]+#is', $last_word)) {
    return $string;
  }
  $almost_last_word = array_pop($words);
  if (preg_match('#[<>]+#is', $almost_last_word)) {
    return $string;
  }
  array_push($words, "<span class='nobr'>".$almost_last_word." ".$last_word."</span>");
  return implode(' ', $words);
}
function no_widows_for_end_tag($input, $end_tag){
  $split_stuff = explode($end_tag, $input);
  $split_stuff = array_map('add_nobr_spans', $split_stuff);
  $result = implode($end_tag, $split_stuff);
  return $result;
}
function no_widows($string) {
  $string = no_widows_for_end_tag($string, "</p>");
  $string = no_widows_for_end_tag($string, "</h3>");
  $string = no_widows_for_end_tag($string, "</h4>");
  $string = no_widows_for_end_tag($string, "</h5>");
  return $string;
}
</code>```

ContentLicense: CC BY-SA 2.5

Body:
Suppose a text shows up as follows on an HTML page (just one word too long to fit on one line):

Lorem ipsum dolores amet foo
bar

How can one avoid with CSS that the last word appears on the last line, and force two (or more)?

Lorem ipsum dolores amet
foo bar

Body:
here's some newbie php that you might use if you wanted to do this in wordpress

function add_nobr_spans($string) {
  $words = explode(' ', $string);
  if (count($words) < 3){
    return $string;
  }
  $last_word = array_pop($words);
  while ($last_word == "") {
    $last_word = array_pop($words);
  }
  if (preg_match('#[<>]+#is', $last_word)) {
    return $string;
  }
  $almost_last_word = array_pop($words);
  if (preg_match('#[<>]+#is', $almost_last_word)) {
    return $string;
  }
  array_push($words, "<span class='nobr'>".$almost_last_word." ".$last_word."</span>");
  return implode(' ', $words);
}
function no_widows_for_end_tag($input, $end_tag){
  $split_stuff = explode($end_tag, $input);
  $split_stuff = array_map('add_nobr_spans', $split_stuff);
  $result = implode($end_tag, $split_stuff);
  return $result;
}
function no_widows($string) {
  $string = no_widows_for_end_tag($string, "</p>");
  $string = no_widows_for_end_tag($string, "</h3>");
  $string = no_widows_for_end_tag($string, "</h4>");
  $string = no_widows_for_end_tag($string, "</h5>");
  return $string;
}


问:
'Provided Authorization Grant is invalid' error while trying to login a dockerized application using wso2
I have dockerized our Angular application which have been using WSO2 as API manager . After doing the configurations, i was able to run the application successfully and able to hit all existing api's. The only issue arises when i tried to use oath2/token api for performing login operation of our customer . Even though, the same code was used to perform authentication earlier(before dockerization) without any issues,now i am getting error as

```<code>{
  "error": "invalid_grant",
  "error_description": "Provided Authorization Grant is invalid"
}
</code>```

Token generation api for login :

```<code>https://<myapplicationurl>:9443/oauth2/token
</code>```

Errors am getting in docker console while trying to login using username 'devtest7@mailinator.com' :

Things to note :

WSO2 AM version is 2.1.0 , WSO2 IS version is 5.3.0

Arguments(headers & parameters) for the request is the same as that
used earlier(except the username and password).

I am able to create a new users and the corresponding user is listed
in Carbondb users list.

The issue exists while trying to login using existing user as well as
newly created users.

I have recently generated new ssl certificate for the application.

Able to login using super admin only . Login using newly created email and username is not working.

I tried solutions seen on stackoverflow which doesn't fixed my issues. Can any one please help?

答:
Thanks for everyone who commented and tried to figure out the solution for the issue i mentioned. I got the resolution for the issue . As  i tried multiple times to login by doing permutations and combinations in configurations, authentication was blocked for me. As a reason, i couldn't login and generate access token . I was able to resolve it by changing a flag in identity.xml file inside IS .
Changed the UserOperationEventListener enabling from 'true' to 'false' .

Before:

```<code><EventListener enable="true" name="org.wso2.carbon.identity.governance.listener.IdentityMgtEventListener" orderId="95" type="org.wso2.carbon.user.core.listener.UserOperationEventListener"/>
</code>```

After:

```<code><EventListener enable="false" name="org.wso2.carbon.identity.governance.listener.IdentityMgtEventListener" orderId="95" type="org.wso2.carbon.user.core.listener.UserOperationEventListener"/>
</code>```

This change allowed me to block the invalid authentication check. We are anyway adding that check from our code side.

Body:
I have dockerized our Angular application which have been using WSO2 as API manager . After doing the configurations, i was able to run the application successfully and able to hit all existing api's. The only issue arises when i tried to use oath2/token api for performing login operation of our customer . Even though, the same code was used to perform authentication earlier(before dockerization) without any issues,now i am getting error as

{
  "error": "invalid_grant",
  "error_description": "Provided Authorization Grant is invalid"
}

Token generation api for login :

https://<myapplicationurl>:9443/oauth2/token

Errors am getting in docker console while trying to login using username 'devtest7@mailinator.com' :

Things to note :

WSO2 AM version is 2.1.0 , WSO2 IS version is 5.3.0

Arguments(headers & parameters) for the request is the same as that
used earlier(except the username and password).

I am able to create a new users and the corresponding user is listed
in Carbondb users list.

The issue exists while trying to login using existing user as well as
newly created users.

I have recently generated new ssl certificate for the application.

Able to login using super admin only . Login using newly created email and username is not working.

I tried solutions seen on stackoverflow which doesn't fixed my issues. Can any one please help?

Body:
Thanks for everyone who commented and tried to figure out the solution for the issue i mentioned. I got the resolution for the issue . As  i tried multiple times to login by doing permutations and combinations in configurations, authentication was blocked for me. As a reason, i couldn't login and generate access token . I was able to resolve it by changing a flag in identity.xml file inside IS .
Changed the UserOperationEventListener enabling from 'true' to 'false' .

Before:

<EventListener enable="true" name="org.wso2.carbon.identity.governance.listener.IdentityMgtEventListener" orderId="95" type="org.wso2.carbon.user.core.listener.UserOperationEventListener"/>

After:

<EventListener enable="false" name="org.wso2.carbon.identity.governance.listener.IdentityMgtEventListener" orderId="95" type="org.wso2.carbon.user.core.listener.UserOperationEventListener"/>

This change allowed me to block the invalid authentication check. We are anyway adding that check from our code side.


问:
AEM 6.5 WKND repo build issues
I created a maven project in batch mode by typing the following command:

mvn archetype:generate -B -DarchetypeGroupId=com.adobe.granite.archetypes -DarchetypeArtifactId=aem-project-archetype -DarchetypeVersion=22 -DgroupId=com.adobe.aem.guides -Dversion=0.0.1-SNAPSHOT -DappsFolderName=wknd -DartifactId=aem-guides-wknd -Dpackage=com.adobe.aem.guides.wknd -DartifactName="WKND Sites Project" -DcomponentGroupName=WKND -DconfFolderName=wknd -DcontentFolderName=wknd -DcssId=wknd -DisSingleCountryWebsite=n -Dlanguage_country=en_us -DoptionAemVersion=6.5.0 -DoptionDispatcherConfig=none -DoptionIncludeErrorHandler=n -DoptionIncludeExaples=y -DoptionIncludeFrontendModule=y -DpackageGroup=wknd -DsiteName="WKND Site"
```

when I type mvn -PautoInstallSinglePackage clean install

I get the following error:

[ERROR] Failed to execute goal on project aem-guides-wknd.ui.apps: Could not resolve dependencies for project com.adobe.aem.guides:aem-guides-wknd.ui.apps:content-package:0.0.1-SNAPSHOT: Could not find artifact com.adobe.aem.guides:aem-guides-wknd.ui.frontend:zip:0.0.1-SNAPSHOT

From the error I guess and when I searched that there is no such artifactid in maven repository. Can someone please help me out here?

答:
You have below options :
1.You Can check your root pom.xml file. There you can comment out ui.frontend module i.e. . After hat you can run your package build command.
2. you can got to each sub-module like core,ui.appss and run following command to build individually mvn clean install -PautoInstallBundle
Let me know if this helps or will look into some other solution.

Title: AEM 6.5 WKND repo build issues

Body:
I created a maven project in batch mode by typing the following command:

mvn archetype:generate -B -DarchetypeGroupId=com.adobe.granite.archetypes -DarchetypeArtifactId=aem-project-archetype -DarchetypeVersion=22 -DgroupId=com.adobe.aem.guides -Dversion=0.0.1-SNAPSHOT -DappsFolderName=wknd -DartifactId=aem-guides-wknd -Dpackage=com.adobe.aem.guides.wknd -DartifactName="WKND Sites Project" -DcomponentGroupName=WKND -DconfFolderName=wknd -DcontentFolderName=wknd -DcssId=wknd -DisSingleCountryWebsite=n -Dlanguage_country=en_us -DoptionAemVersion=6.5.0 -DoptionDispatcherConfig=none -DoptionIncludeErrorHandler=n -DoptionIncludeExaples=y -DoptionIncludeFrontendModule=y -DpackageGroup=wknd -DsiteName="WKND Site"

when I type mvn -PautoInstallSinglePackage clean install

I get the following error:

[ERROR] Failed to execute goal on project aem-guides-wknd.ui.apps: Could not resolve dependencies for project com.adobe.aem.guides:aem-guides-wknd.ui.apps:content-package:0.0.1-SNAPSHOT: Could not find artifact com.adobe.aem.guides:aem-guides-wknd.ui.frontend:zip:0.0.1-SNAPSHOT

From the error I guess and when I searched that there is no such artifactid in maven repository. Can someone please help me out here?

Body:
You have below options :
1.You Can check your root pom.xml file. There you can comment out ui.frontend module i.e. <!--module>  ui.frontend  </module-->. After hat you can run your package build command.
2. you can got to each sub-module like core,ui.appss and run following command to build individually mvn clean install -PautoInstallBundle
Let me know if this helps or will look into some other solution.


问:
Why barryvdh/laravel-dompdf Stuck or won't process HTML to PDF when using @font-face CSS?
When using dompdf library for generating html to pdf i don't find any problem. But when i adding the @font-face in the CSS and providing the .ttf file for "Courier New" Font, the dompdf was stuck at some process and it won't finish generating the pdf file.

i'm adding this line to my style

@font-face {
      font-family: 'CourierNew';
      src: url('{{ asset('fonts/cour.ttf') }}');
 }

and i'm putting the Courier New TrueType file at the public/fonts/ folder

My Project using :

Laravel : 5.8

PHP : 7.3.11

barryvdh/laravel-dompdf : ^0.8.4

Did anyone encounter such a problem or have a solution regarding this problem?

Thank you in advance.

答:
You should try to temporarily increase the maximum execution with

```<code>ini_set('max_execution_time', 300);

// your pdf generation code

ini_restore('max_execution_time');
</code>```

just to make sure that the issue is not that the pdf file you are trying to generate is too big and that the added time to load the font is enough to bring it over the execution time limit.

Body:
You should try to temporarily increase the maximum execution with

ini_set('max_execution_time', 300);

// your pdf generation code

ini_restore('max_execution_time');

just to make sure that the issue is not that the pdf file you are trying to generate is too big and that the added time to load the font is enough to bring it over the execution time limit.


问:
Magento 1.3.x Quick Search problem
We have built a website http://www.goshopping.pk/ (sorry had to post the link as its important for this question).

The quick search is not working as it should. For example, search "Nokia" and you will get all sorts of results. Search "Dell" and you get the same results. However, searching exact matches like "nokia 6600" or "Intel Core 2 DUO" or "Dell Inspiron" works perfectly fine.

We have rebuilt the search index, emptied the cache etc but it has no effect. What are we missing?

Help appreciated. Thanks!

答:
One quick tip I normally advise people is to remove the description from quick search results in Catalog > Manage Attributes > Attributes

Obviously the description contains all sorts of words and can dilute search results. See if that improves anything.

Also in Configuration > Catalog I normally change the Search Type to Fulltext for more accurate results.

Title: Magento 1.3.x Quick Search problem


问:
checking for at least 1 number between 0-9 in a given password using c#
I'm trying to cycle through each char in a string to see if any of them are a number between 0-9. I'm getting an index out of bounds error for the numCheck array, so I know that my issue is that when I attempt to run, the IDE expects the length of txt_Pass.Text to = the number of chars I have in my array. This is wrong, but I'm not sure how to fix it. Would I need to use a vector since I'm not sure how long the input password will be? Or am I totally off?

```<code>        char[] numCheck = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

        for (int i = 0; i < txt_Pass.Text.Length; i++)
        {
            if (txt_Pass.Text[i] != numCheck[i])
            {
                lbl_Form1_NumError.Visible = true;
            }
            lbl_Form1_NumError.Visible = false;
        }
</code>```

'''

答:
I think the simplest solution combines LINQ's Any and Char.IsDigit:

```<code>lbl_Form1_NumError.Visible = !txt_Pass.Text.Any(char.IsDigit);
</code>```

Body:
I'm trying to cycle through each char in a string to see if any of them are a number between 0-9. I'm getting an index out of bounds error for the numCheck array, so I know that my issue is that when I attempt to run, the IDE expects the length of txt_Pass.Text to = the number of chars I have in my array. This is wrong, but I'm not sure how to fix it. Would I need to use a vector since I'm not sure how long the input password will be? Or am I totally off?

        char[] numCheck = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

        for (int i = 0; i < txt_Pass.Text.Length; i++)
        {
            if (txt_Pass.Text[i] != numCheck[i])
            {
                lbl_Form1_NumError.Visible = true;
            }
            lbl_Form1_NumError.Visible = false;
        }

'''

Body:
I think the simplest solution combines LINQ's Any and Char.IsDigit:

lbl_Form1_NumError.Visible = !txt_Pass.Text.Any(char.IsDigit);


问:
Spring Boot - Ambiguous Mapping
Pulling my hair out over this one, makes absolutely no sense

```<code>@RestController("/firmwareAShkcwdsdskl")
public class FirmwareController {
    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Result> createNewFirmwareVersion(DetailedFirmwareVersionRequest detailedFirmwareVersionRequest) {
    // Code Block
    }
}
</code>```

```<code>
@RestController("/jobs/firmwareUpgrade")
public class FirmwareUpgradeController {
    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Result> createNewJob(DetailedFirmwareUpgradeRequest detailedFirmwareUpgradeRequest) {
    // Code Block
    }
}
</code>```

Attempting to start my Spring Boot application with these two controllers is throwing the following error

```<code>Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map '/jobs/firmwareUpgrade' method
eshepherd.admin.api.controller.FirmwareUpgradeController#createNewJob(DetailedFirmwareUpgradeRequest)
to {POST , produces [application/json]}: There is already '/firmwareAShkcwdsdskl' bean method
eshepherd.admin.api.controller.FirmwareController#createNewFirmwareVersion(DetailedFirmwareVersionRequest) mapped.
</code>```

The class name, method name, request mapping and parameters are all unique, I just don't understand it.

As you can tell I've descended into pure frustration trying all kinds of string changes to just get it to work initially, but if anyone could help me identify the problem I'd be extremely grateful.

Edit: Using Spring-Boot 2.2.4

答:
The framework is complaining that there exists a post path / from the 2 controllers where the mapping is not specified for any one of them hence it cannot distinguish the exact path.

Can you please try the following code:

```<code>@RestController
@RequestMapping("/firmwareAShkcwdsdskl")
public class FirmwareController {
    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Result> createNewFirmwareVersion(DetailedFirmwareVersionRequest detailedFirmwareVersionRequest) {
    // Code Block
    }
}

@RestController
@RequestMapping("/jobs/firmwareUpgrade")
public class FirmwareUpgradeController {
    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Result> createNewJob(DetailedFirmwareUpgradeRequest detailedFirmwareUpgradeRequest) {
    // Code Block
    }
}
</code>```

@RequestMapping at controller level should define the root path for all the APIs in that path say for example I have to create a controller related to feeds API I would do:

```<code>@RestController
@RequestMapping("/api/feeds")
class FeedsController {
    //Constructor inject fields here
    @Autowired
    FeedsController() {

    }

    @GetMapping("/")
    ResponseEntity<Map<String, String>> get() {
        return ResponseEntity.ok(Collections.emptyMap());
    }

    @PostMapping("/")
    ResponseEntity<Map<String, String>> post() {
        return ResponseEntity.ok(Collections.emptyMap());
    }

    /**
     * Standard way to implement delete is to soft delete
     * @return
     */
    @DeleteMapping("/")
    ResponseEntity<Map<String, String>> delete() {
        return ResponseEntity.ok(Collections.emptyMap());
    }
}
</code>```

Title: Spring Boot - Ambiguous Mapping

Body:
Pulling my hair out over this one, makes absolutely no sense

@RestController("/firmwareAShkcwdsdskl")
public class FirmwareController {
    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Result> createNewFirmwareVersion(DetailedFirmwareVersionRequest detailedFirmwareVersionRequest) {
    // Code Block
    }
}

@RestController("/jobs/firmwareUpgrade")
public class FirmwareUpgradeController {
    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Result> createNewJob(DetailedFirmwareUpgradeRequest detailedFirmwareUpgradeRequest) {
    // Code Block
    }
}

Attempting to start my Spring Boot application with these two controllers is throwing the following error

Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map '/jobs/firmwareUpgrade' method
eshepherd.admin.api.controller.FirmwareUpgradeController#createNewJob(DetailedFirmwareUpgradeRequest)
to {POST , produces [application/json]}: There is already '/firmwareAShkcwdsdskl' bean method
eshepherd.admin.api.controller.FirmwareController#createNewFirmwareVersion(DetailedFirmwareVersionRequest) mapped.

The class name, method name, request mapping and parameters are all unique, I just don't understand it.

As you can tell I've descended into pure frustration trying all kinds of string changes to just get it to work initially, but if anyone could help me identify the problem I'd be extremely grateful.

Edit: Using Spring-Boot 2.2.4

Body:
The framework is complaining that there exists a post path / from the 2 controllers where the mapping is not specified for any one of them hence it cannot distinguish the exact path.

Can you please try the following code:

@RestController
@RequestMapping("/firmwareAShkcwdsdskl")
public class FirmwareController {
    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Result> createNewFirmwareVersion(DetailedFirmwareVersionRequest detailedFirmwareVersionRequest) {
    // Code Block
    }
}

@RestController
@RequestMapping("/jobs/firmwareUpgrade")
public class FirmwareUpgradeController {
    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Result> createNewJob(DetailedFirmwareUpgradeRequest detailedFirmwareUpgradeRequest) {
    // Code Block
    }
}

@RequestMapping at controller level should define the root path for all the APIs in that path say for example I have to create a controller related to feeds API I would do:

@RestController
@RequestMapping("/api/feeds")
class FeedsController {
    //Constructor inject fields here
    @Autowired
    FeedsController() {

    }

    @GetMapping("/")
    ResponseEntity<Map<String, String>> get() {
        return ResponseEntity.ok(Collections.emptyMap());
    }

    @PostMapping("/")
    ResponseEntity<Map<String, String>> post() {
        return ResponseEntity.ok(Collections.emptyMap());
    }

    /**
     * Standard way to implement delete is to soft delete
     * @return
     */
    @DeleteMapping("/")
    ResponseEntity<Map<String, String>> delete() {
        return ResponseEntity.ok(Collections.emptyMap());
    }
}