问:
Scrapy: Selector for text between two HTML elements..?
I am currently using scrapy to scrape a website. The Website is a list of profiles. So the Spider click on every link in the list (which is one profile) and then extract the data, comes back and clicks on the next one etc. This is how I structured it:

```<code>class Profiles(scrapy.Spider):
    name = 'profiles'
    allowed_domains = ['url.com']
    start_urls = ['https://www.url/profiles/']

    def parse(self, response):
        for profile in response.css('.herald-entry-content p'):
            url = response.urljoin(profile.css('a::attr(href)').extract_first())
            yield scrapy.Request(url=url, callback=self.parse_profile, dont_filter=True)

    def parse_profile(self, response):
        birth_name = response.xpath("//*[@id='post-19807']/div/div[1]/div/div[2]/div/p[1]/text()[1]").extract()
        profile = Profile(
            birth_name=birth_name
        )
        yield profile
</code>```

While working, I have encountered a problem with fetching certain data. Here is a snippet of what the structure looks like on the actual profile page:

```<code>    <div class="herald-entry-content">
        <p><b>Profile: Facts<br>
        </b><br>
            <span>Stage Name:</span> Any name<br>
            <span>Birth Name:</span> Any name<br>
            <span>Birthday:</span> July 10, 1994<br>
            <span>Zodiac Sign:</span> Cancer<br>
            <span>Height:</span> 178 cm <br>
        </p>
    </div>
</code>```

I would like to extract the Birth Name here, but using birth_name = response.css(".herald-entry-content p span::Text") will give me the text of the span element, which is not what i want. I tried playing around with xpath (right click and Copy Xpath in chrome) which gave me //*[@id="post-19807"]/div/div[1]/div/div[2]/div/p[1]/text()[2]
Now, this works, but the post-id is specific to this page, and I do loop over the other profiles as well, so that value will change a lot. Is there any way I can tell the spider to look for the element and get the ID itself? Im kind of lost how to proceed with this.

Thanks a lot!

答:
This might be a case that you have to fallback to a regular expression.

Without knowing the full structure of the page it is hard to give you exactly what you need, but here is an example using the snippet you gave

import scrapy

sel = scrapy.Selector(text="""

Profile: Facts

            Stage Name: Any name

            Birth Name: Any name

            Birthday: July 10, 1994

            Zodiac Sign: Cancer

            Height: 178 cm

""")

info = sel.re("(.+):\s(.+)
")
output = dict(zip(*[iter(info)] * 2))
print(output)
```
<p>will give you</p>
```{'Stage Name': 'Any name',
 'Birth Name': 'Any name',
 'Birthday': 'July 10, 1994',
 'Zodiac Sign': 'Cancer',
 'Height': '178 cm '}
```
<p>The slightly cryptic <code>dict(zip(*[iter(info)] * 2))</code> comes from here.</p>
<p>Note you shouldn't have to use the <code>scrapy.Selector</code> directly, you should be able to do something like</p>
```def parse_profile(self, response):
    herald_content = response.xpath('//div[@class="herald-entry-content"]')
    info = herald_content.re("(.+):\s(.+)
")
    # and so on from example above...
```

Body:
I am currently using scrapy to scrape a website. The Website is a list of profiles. So the Spider click on every link in the list (which is one profile) and then extract the data, comes back and clicks on the next one etc. This is how I structured it:

class Profiles(scrapy.Spider):
    name = 'profiles'
    allowed_domains = ['url.com']
    start_urls = ['https://www.url/profiles/']

    def parse(self, response):
        for profile in response.css('.herald-entry-content p'):
            url = response.urljoin(profile.css('a::attr(href)').extract_first())
            yield scrapy.Request(url=url, callback=self.parse_profile, dont_filter=True)

    def parse_profile(self, response):
        birth_name = response.xpath("//*[@id='post-19807']/div/div[1]/div/div[2]/div/p[1]/text()[1]").extract()
        profile = Profile(
            birth_name=birth_name
        )
        yield profile

While working, I have encountered a problem with fetching certain data. Here is a snippet of what the structure looks like on the actual profile page:

Profile: Facts

            Stage Name: Any name

            Birth Name: Any name

            Birthday: July 10, 1994

            Zodiac Sign: Cancer

            Height: 178 cm

I would like to extract the Birth Name here, but using birth_name = response.css(".herald-entry-content p span::Text") will give me the text of the span element, which is not what i want. I tried playing around with xpath (right click and Copy Xpath in chrome) which gave me //*[@id="post-19807"]/div/div[1]/div/div[2]/div/p[1]/text()[2]
Now, this works, but the post-id is specific to this page, and I do loop over the other profiles as well, so that value will change a lot. Is there any way I can tell the spider to look for the element and get the ID itself? Im kind of lost how to proceed with this.

Thanks a lot!

Body:
This might be a case that you have to fallback to a regular expression.

Without knowing the full structure of the page it is hard to give you exactly what you need, but here is an example using the snippet you gave

import scrapy

sel = scrapy.Selector(text="""
 <div class="herald-entry-content">
        <p><b>Profile: Facts<br>
        </b><br>
            <span>Stage Name:</span> Any name<br>
            <span>Birth Name:</span> Any name<br>
            <span>Birthday:</span> July 10, 1994<br>
            <span>Zodiac Sign:</span> Cancer<br>
            <span>Height:</span> 178 cm <br>
        </p>
    </div>
""")

info = sel.re("<span>(.+):</span>\s(.+)<br>")
output = dict(zip(*[iter(info)] * 2))
print(output)

will give you

{'Stage Name': 'Any name',
 'Birth Name': 'Any name',
 'Birthday': 'July 10, 1994',
 'Zodiac Sign': 'Cancer',
 'Height': '178 cm '}

The slightly cryptic dict(zip(*[iter(info)] * 2)) comes from here.

Note you shouldn't have to use the scrapy.Selector directly, you should be able to do something like

def parse_profile(self, response):
    herald_content = response.xpath('//div[@class="herald-entry-content"]')
    info = herald_content.re("<span>(.+):</span>\s(.+)<br>")
    # and so on from example above...


来源: stackexchange

问:
Module exports for Express Router / Functions
Hi I am trying to export both a function (so that other routes may use this function to verify certs and also a the express router so that I can add it to my main class to mount the route on. This is because I believe the function and the route both serve the same sort of "functionality" and I want to encapsulate it in one file, so I want to export both the function and the router to use! Here is the following code of which I get an error... Note I WANT to do verifytoken.router to refer to the router and then verifytoken.verify to refer to the function in other files

/routes/verifytoken.js file

```<code>const router = require('express').Router();
const jwt = require('jsonwebtoken');

function verify (req, res, next) {
    const token = req.header("auth-token");
    if (!token) return res.status(401).send("Access Denied");

    try {
        const verified = jwt.verify(token, process.env.TOKEN_SECRET);
        req.user = verified;
        next();
    } catch (error) {
        res.status(400).send("Invalid Token")
    }
}

router.get("/tester", (req, res) => {
    res.status(200).send("validation please work bro");
});

module.exports = {
    verify:verify,
    router:router
}
</code>```

my main index.js file

```<code>const express = require('express');

//import routes
const verifytoken = require('./routes/verifytoken')

const app = express();

//route middlewares
app.use("/api/user". verifytoken.router);

app.listen(3000 , () => console.log('Server Running...'))
</code>```

The stack trace is :

```<code>app.use("/api/user". verifytoken.router);
                                 ^

TypeError: Cannot read property 'router' of undefined
</code>```

答:
I think there's another typo (dot), try:</p>

```<code>app.use("/api/user", verifytoken.router);
</code>```

ContentLicense: CC BY-SA 4.0

Body:
Hi I am trying to export both a function (so that other routes may use this function to verify certs and also a the express router so that I can add it to my main class to mount the route on. This is because I believe the function and the route both serve the same sort of "functionality" and I want to encapsulate it in one file, so I want to export both the function and the router to use! Here is the following code of which I get an error... Note I WANT to do verifytoken.router to refer to the router and then verifytoken.verify to refer to the function in other files

/routes/verifytoken.js file

const router = require('express').Router();
const jwt = require('jsonwebtoken');

function verify (req, res, next) {
    const token = req.header("auth-token");
    if (!token) return res.status(401).send("Access Denied");

    try {
        const verified = jwt.verify(token, process.env.TOKEN_SECRET);
        req.user = verified;
        next();
    } catch (error) {
        res.status(400).send("Invalid Token")
    }
}

router.get("/tester", (req, res) => {
    res.status(200).send("validation please work bro");
});

module.exports = {
    verify:verify,
    router:router
}

my main index.js file

const express = require('express');

//import routes
const verifytoken = require('./routes/verifytoken')

const app = express();

//route middlewares
app.use("/api/user". verifytoken.router);

app.listen(3000 , () => console.log('Server Running...'))

The stack trace is :

app.use("/api/user". verifytoken.router);
                                 ^

TypeError: Cannot read property 'router' of undefined

Body:
I think there's another typo (dot), try:

app.use("/api/user", verifytoken.router);


问:
PUT/POST in Laravel
I'm brand new to Laravel and am working my way through the [Laravel 6 from Scratch][1] course over at Laracasts. The course is free but I can't afford a Laracasts membership so I can't ask questions there.

I've finished Section 6 of the course, Controller Techniques, and am having unexpected problems trying to extend the work we've done so far to add a few new features. The course has students build pages that let a user show a list of articles, look at an individual article, create and save a new article, and update and save an existing article. The course work envisioned a very simple article containing just an ID (auto-incremented in the database and not visible to the web user), a title, an excerpt and a body and I got all of the features working for that, including updating an existing article and saving it.

The update form sets method to POST but then uses a @METHOD('PUT') directive to tell the browser that it is actually supposed to do a PUT. This worked perfectly in the original code. However, now that I've added two more fields to the form, when I click Submit after editing an existing record, the save fails with this message:

```<code>Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
The PUT method is not supported for this route. Supported methods: GET, HEAD, POST.
http://localhost:8000/articles
</code>```

I don't understand why adding two fields to the form would cause this to break. Can someone enlighten me? I added the two new fields/columns to the migration and ran migrate:rollback and migrate. I've also added the new fields/columns to the fillable attribute and added validations for them in the ArticlesController.

Here is my routing:

```<code>Route::get('/articles', 'ArticlesController@index');
Route::post('/articles', 'ArticlesController@store');
Route::get('/articles/create', 'ArticlesController@create');
Route::get('/articles/{article}', 'ArticlesController@show');
Route::get('/articles/{article}/edit', 'ArticlesController@edit');
Route::put('/articles/{article}', 'ArticlesController@update');
//Route::delete('/articles/{article}', ArticlesController@destroy');
</code>```

This is my ArticlesController:

```<code><?php

namespace App\Http\Controllers;

use App\Article;
use Illuminate\Http\Request;

class ArticlesController extends Controller
{
public function index()
{
    $articles = Article::latest()->get();
    return view ('articles.index', ['articles' => $articles]);
}

public function show(Article $article)
{
    return view('articles.show', ['article' => $article]);
}

public function create()
{
    return view('articles.create');
}

public function store()
{
    //Stores a NEW article
    Article::create($this->validateArticle());
    return redirect('/articles');
}

public function edit(Article $article)
{
    return view('articles.edit', ['article' => $article]);
}

public function update(Article $article)
{
    //Updates an EXISTING article
    $article->update($this->validateArticle());
    return redirect('/articles/', $article->id);
}

public function validateArticle()
{
    return request()->validate([
        'title' => ['required', 'min:5', 'max:20'],
        'author' => ['required', 'min:5', 'max:30'],
        'photopath' => ['required', 'min:10', 'max:100'],
        'excerpt' => ['required', 'min:10', 'max:50'],
        'body' => ['required', 'min:50', 'max:500']
    ]);
}

public function destroy(Article $article)
{
    //Display existing record with "Are you sure you want to delete this? Delete|Cancel" option
    //If user chooses Delete, delete the record
    //If user chooses Cancel, return to the list of articles
}
}
</code>```

Here's my edit form, edit.blade.php:

```<code>@extends('layout')

@section('content')
<div id="wrapper">
<div id="page" class="container">
    <h1>Update Article</h1>
    <form method="POST" action="/articles">
        @csrf
        @method('PUT')
        <div class="form-group">
            <label class="label" for="title">Title</label>
            <div class="control">
                <input class="form-control @error('title') errorborder @enderror" type="text" name="title" id="title" value="{{ $article->title }}">
                @error('title')
                    <p class="errortext">{{ $errors->first('title') }}</p>
                @enderror
            </div>
        </div>
        <div class="form-group">
            <label class="label" for="author">Author</label>
            <div class="control">
                <input class="form-control @error('author') errorborder @enderror" type="text" name="author" id="author" value="{{ $article->author }}">
                @error('title')
                    <p class="errortext">{{ $errors->first('author') }}</p>
                @enderror
            </div>
        </div>
        <div class="form-group">
            <label class="label" for="photopath">Path to Photo</label>
            <div class="control">
                <input class="form-control @error('photopath') errorborder @enderror" type="text" name="photopath" id="photopath" value="{{ $article->photopath }}">
                @error('title')
                    <p class="errortext">{{ $errors->first('photopath') }}</p>
                @enderror
            </div>
        </div>
        <div class="form-group">
            <label class="label" for="excerpt">Excerpt</label>
            <div class="control">
                <textarea class="form-control @error('excerpt') errorborder @enderror" name="excerpt" id="excerpt">{{ $article->excerpt }}</textarea>
                @error('excerpt')
                    <p class="errortext">{{ $errors->first('excerpt') }}</p>
                @enderror
            </div>
        </div>
        <div class="form-group">
            <label class="label" for="body">Body</label>
            <div class="control">
                <textarea class="form-control @error('body') errorborder @enderror" name="body" id="body">{{ $article->body }}</textarea>
                @error('body')
                    <p class="errortext">{{ $errors->first('body') }}</p>
                @enderror
            </div>
        </div>
        <div class="control">
            <button class="btn btn-primary" type="submit">Submit</button>
        </div>
    </form>
</div>
</div>
@endsection
</code>```

```<code>Is there anything else you need to see?

  [1]: https://laracasts.com/series/laravel-6-from-scratch/episodes/33?autoplay=true
</code>```

答:
Your Laravel route is:

```<code>Route::put('/articles/{article}', 'ArticlesController@update');
</code>```

So your form action url should match that uri:

```<code><form action="{{ url('/articles/'.$article->id) }}">
</code>```

where the {article} parameter is the record id (you can read more about in the docs here).

Then in your controller update() method, you have:

```<code>return redirect('/articles/', $article->id);
</code>```

which means redirect to /articles with status code $article->id (you can read more about in the docs here). I think you are trying to redirect to the show route, which is:

```<code>Route::get('/articles/{article}', 'ArticlesController@show');
</code>```

So change the , (comma) to a . (dot) to concatenate the article id with the uri:

```<code>return redirect('/articles/' . $article->id);
</code>```

Title: PUT/POST in Laravel

Body:
I'm brand new to Laravel and am working my way through the [Laravel 6 from Scratch][1] course over at Laracasts. The course is free but I can't afford a Laracasts membership so I can't ask questions there.

I've finished Section 6 of the course, Controller Techniques, and am having unexpected problems trying to extend the work we've done so far to add a few new features. The course has students build pages that let a user show a list of articles, look at an individual article, create and save a new article, and update and save an existing article. The course work envisioned a very simple article containing just an ID (auto-incremented in the database and not visible to the web user), a title, an excerpt and a body and I got all of the features working for that, including updating an existing article and saving it.

The update form sets method to POST but then uses a @METHOD('PUT') directive to tell the browser that it is actually supposed to do a PUT. This worked perfectly in the original code. However, now that I've added two more fields to the form, when I click Submit after editing an existing record, the save fails with this message:

Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
The PUT method is not supported for this route. Supported methods: GET, HEAD, POST.
http://localhost:8000/articles

I don't understand why adding two fields to the form would cause this to break. Can someone enlighten me? I added the two new fields/columns to the migration and ran migrate:rollback and migrate. I've also added the new fields/columns to the fillable attribute and added validations for them in the ArticlesController.

Here is my routing:

Route::get('/articles', 'ArticlesController@index');
Route::post('/articles', 'ArticlesController@store');
Route::get('/articles/create', 'ArticlesController@create');
Route::get('/articles/{article}', 'ArticlesController@show');
Route::get('/articles/{article}/edit', 'ArticlesController@edit');
Route::put('/articles/{article}', 'ArticlesController@update');
//Route::delete('/articles/{article}', ArticlesController@destroy');

This is my ArticlesController:

<?php

namespace App\Http\Controllers;

use App\Article;
use Illuminate\Http\Request;

class ArticlesController extends Controller
{
public function index()
{
    $articles = Article::latest()->get();
    return view ('articles.index', ['articles' => $articles]);
}

public function show(Article $article)
{
    return view('articles.show', ['article' => $article]);
}

public function create()
{
    return view('articles.create');
}

public function store()
{
    //Stores a NEW article
    Article::create($this->validateArticle());
    return redirect('/articles');
}

public function edit(Article $article)
{
    return view('articles.edit', ['article' => $article]);
}

public function update(Article $article)
{
    //Updates an EXISTING article
    $article->update($this->validateArticle());
    return redirect('/articles/', $article->id);
}

public function validateArticle()
{
    return request()->validate([
        'title' => ['required', 'min:5', 'max:20'],
        'author' => ['required', 'min:5', 'max:30'],
        'photopath' => ['required', 'min:10', 'max:100'],
        'excerpt' => ['required', 'min:10', 'max:50'],
        'body' => ['required', 'min:50', 'max:500']
    ]);
}

public function destroy(Article $article)
{
    //Display existing record with "Are you sure you want to delete this? Delete|Cancel" option
    //If user chooses Delete, delete the record
    //If user chooses Cancel, return to the list of articles
}
}

Here's my edit form, edit.blade.php:

@extends('layout')

@section('content')

Update Article

    <form method="POST" action="/articles">
        @csrf
        @method('PUT')

            Title

                <input class="form-control @error('title') errorborder @enderror" type="text" name="title" id="title" value="{{ $article->title }}">
                @error('title')

{{ $errors->first('title') }}

                @enderror

            Author

                <input class="form-control @error('author') errorborder @enderror" type="text" name="author" id="author" value="{{ $article->author }}">
                @error('title')

{{ $errors->first('author') }}

                @enderror

            Path to Photo

                <input class="form-control @error('photopath') errorborder @enderror" type="text" name="photopath" id="photopath" value="{{ $article->photopath }}">
                @error('title')

{{ $errors->first('photopath') }}

                @enderror

            Excerpt

                <textarea class="form-control @error('excerpt') errorborder @enderror" name="excerpt" id="excerpt">{{ $article->excerpt }}</textarea>
                @error('excerpt')

{{ $errors->first('excerpt') }}

                @enderror

            Body

                <textarea class="form-control @error('body') errorborder @enderror" name="body" id="body">{{ $article->body }}</textarea>
                @error('body')

{{ $errors->first('body') }}

                @enderror

            <button class="btn btn-primary" type="submit">Submit</button>

    </form>

@endsection

Is there anything else you need to see?

  [1]: https://laracasts.com/series/laravel-6-from-scratch/episodes/33?autoplay=true

Body:
Your Laravel route is:

Route::put('/articles/{article}', 'ArticlesController@update');

So your form action url should match that uri:

<form action="{{ url('/articles/'.$article->id) }}">

where the {article} parameter is the record id (you can read more about in the docs here).

Then in your controller update() method, you have:

return redirect('/articles/', $article->id);

which means redirect to /articles with status code $article->id (you can read more about in the docs here). I think you are trying to redirect to the show route, which is:

Route::get('/articles/{article}', 'ArticlesController@show');

So change the , (comma) to a . (dot) to concatenate the article id with the uri:

return redirect('/articles/' . $article->id);


问:
React TypeError: teams.map is not a function
I getting the TypeError: teams.map is not a function while trying to fetch and paint elements from an api. I haven't been able to find the error.

I'm trying to build a simple app with premier league teams and details about them. I'm still writing because stack overflow won't let me publish my question

leagueTeams Component

import React, { useState } from 'react';

function LeagueTeams() {
  const url = 'https://www.thesportsdb.com/api/v1/json/1/search_all_teams.php?l=English%20Premier%20League'

  const [teams, setTeams] = useState(null)

  fetch(url)
    .then(res => res.json())
    .then(data => setTeams(data))

  return (

    {teams && teams.map((team, index)=>(

{team.strTeam}

)

    )}

  )
}

export default LeagueTeams```
<pre class="snippet-code-html lang-html prettyprint-override"><code><script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script></code>```

App.js

import React from 'react';
import './App.css';
import LeagueTeams from './components/leagueTeams'

function App() {

return(
  <>

Premier League Teams!

  <LeagueTeams />
  </>
)
  }

export default App;```

答:
The error message is teams.map is not a function, this is because teams is not actually an array:

```<code>const url = 'https://www.thesportsdb.com/api/v1/json/1/search_all_teams.php?l=English%20Premier%20League'
</code>```

This does not return an array, it returns an object:

```<code>{"teams":[{"idTeam":"133604","idSoccerXML":"9","idAPIfootball":"42","i....
</code>```

Which means you should update the state via:

```<code>setTeams(data.teams);
</code>```

Working CodeSandbox here https://codesandbox.io/s/recursing-jepsen-odc65

Body:
I getting the TypeError: teams.map is not a function while trying to fetch and paint elements from an api. I haven't been able to find the error.

I'm trying to build a simple app with premier league teams and details about them. I'm still writing because stack overflow won't let me publish my question

leagueTeams Component

import React, { useState } from 'react';

function LeagueTeams() {
  const url = 'https://www.thesportsdb.com/api/v1/json/1/search_all_teams.php?l=English%20Premier%20League'

  const [teams, setTeams] = useState(null)

  fetch(url)
    .then(res => res.json())
    .then(data => setTeams(data))

  return (

    {teams && teams.map((team, index)=>(

{team.strTeam}

)

    )}

  )
}

export default LeagueTeams

App.js

import React from 'react';
import './App.css';
import LeagueTeams from './components/leagueTeams'

function App() {

return(
  <>

Premier League Teams!

  <LeagueTeams />
  </>
)
  }

export default App;

Body:
The error message is teams.map is not a function, this is because teams is not actually an array:

const url = 'https://www.thesportsdb.com/api/v1/json/1/search_all_teams.php?l=English%20Premier%20League'

This does not return an array, it returns an object:

{"teams":[{"idTeam":"133604","idSoccerXML":"9","idAPIfootball":"42","i....

Which means you should update the state via:

setTeams(data.teams);

Working CodeSandbox here https://codesandbox.io/s/recursing-jepsen-odc65


问:
Is there any way to create multiple records at once in BMC Remedy using Java?
Is it good to get the ARServerUser context once and call the setEntry method multiple times in the loop or, is there any better way to do this?</p>

答:
The API provides this. Look in the ARServerUser javadoc:

It is used like this:

```<code>//connect to AR server
ARServerUser server = new ARServerUser();
server.setServer("localhost");
server.setUser("Demo");
server.setPassword("");
// begin bulk transaction
server.beginBulkEntryTransaction();
//create and submit Entry Objects

for(int x = 0; x < 10; x++){
  try {
        Entry entry = new Entry();
        entry.put(Constants.AR_CORE_SUBMITTER, new Value(submitter));
        entry.put(Constants.AR_CORE_STATUS,new Value(status, DataType.ENUM));
        entry.put(Constants.AR_CORE_SHORT_DESCRIPTION, new Value(shortDesc));
        entryIdOut = server.createEntry(formName, entry);
    } catch (ARException e) {
        ARExceptionHandler(e, "Cannot create the entry." );
    }
}
//Commit Bulk transaction: all entries are saved to Remedy
 List<BulkEntryReturn>  bulkIds = server.endBulkEntryTransaction(Constants.AR_BULK_ENTRY_ACTION_SEND);
 //bulkIds now contains all the entry Ids for your committed entries
</code>```

Note that the code above has some uninitialized variables, so will not run as is (and may throw an ARBulkException)

Body:
The API provides this. Look in the ARServerUser javadoc:

It is used like this:

//connect to AR server
ARServerUser server = new ARServerUser();
server.setServer("localhost");
server.setUser("Demo");
server.setPassword("");
// begin bulk transaction
server.beginBulkEntryTransaction();
//create and submit Entry Objects

for(int x = 0; x < 10; x++){
  try {
        Entry entry = new Entry();
        entry.put(Constants.AR_CORE_SUBMITTER, new Value(submitter));
        entry.put(Constants.AR_CORE_STATUS,new Value(status, DataType.ENUM));
        entry.put(Constants.AR_CORE_SHORT_DESCRIPTION, new Value(shortDesc));
        entryIdOut = server.createEntry(formName, entry);
    } catch (ARException e) {
        ARExceptionHandler(e, "Cannot create the entry." );
    }
}
//Commit Bulk transaction: all entries are saved to Remedy
 List<BulkEntryReturn>  bulkIds = server.endBulkEntryTransaction(Constants.AR_BULK_ENTRY_ACTION_SEND);
 //bulkIds now contains all the entry Ids for your committed entries

Note that the code above has some uninitialized variables, so will not run as is (and may throw an ARBulkException)


问:
complex With query on SQL Server 2008
Could you please explain what do these lines of code do?

Table 1: INT,VARCHAR,FLOAT

```<code>ID Name  value
---------------------------
1   a1  32116580
2   a2  50785384
3   a3  54327508
4   a4  61030844

;with coords(i,row,col,total,N) as (
select 1,1,1,N.N*(N.N+1)/2, N.N
from (select count(*) N from table1) N
union all
select i+1,
       case when col+1>N then row+1 else row end,
       case when col+1>N then row+1 else col+1 end,
       total, N
from coords
where i<total
)
</code>```

I know with provides a way to write auxiliary statements for use in a larger query, so it is like if I declare some variables I would use, but after that I am a little confused...And Why the use of case for getting row and col; Also why in the case there are two:  case when col+1>N then row+1 else , How does SQL know when to do one case or the other?...

```<code>i  row col total N
--------------------
1   1   1   10   4
2   1   2   10   4
3   1   3   10   4
4   1   4   10   4
5   2   2   10   4
6   2   3   10   4
7   2   4   10   4
8   3   3   10   4
9   3   4   10   4
10  4   4   10   4
</code>```

答:
It is creating a common table expression.

It is basically create a temporary table structure from the select query within. The select statement is doing the following:

1) Selecting a bunch of default values 1,1,1 and a calculation N.N*(N.N+1)/2 and finally a value from the N table N.N

2) UNIONing in another select statement.

3) The second select is doing some conditional output with the case statements.

ContentLicense: CC BY-SA 3.0

Title: complex With query on SQL Server 2008

Body:
Could you please explain what do these lines of code do?

Table 1: INT,VARCHAR,FLOAT

ID Name  value
---------------------------
1   a1  32116580
2   a2  50785384
3   a3  54327508
4   a4  61030844

;with coords(i,row,col,total,N) as (
select 1,1,1,N.N*(N.N+1)/2, N.N
from (select count(*) N from table1) N
union all
select i+1,
       case when col+1>N then row+1 else row end,
       case when col+1>N then row+1 else col+1 end,
       total, N
from coords
where i<total
)

I know with provides a way to write auxiliary statements for use in a larger query, so it is like if I declare some variables I would use, but after that I am a little confused...And Why the use of case for getting row and col; Also why in the case there are two:  case when col+1>N then row+1 else , How does SQL know when to do one case or the other?...

i  row col total N
--------------------
1   1   1   10   4
2   1   2   10   4
3   1   3   10   4
4   1   4   10   4
5   2   2   10   4
6   2   3   10   4
7   2   4   10   4
8   3   3   10   4
9   3   4   10   4
10  4   4   10   4


问:
Hyper-V: Create shared folder between host and guest with internal network
Set up:

Host: Windows 10 Enterprise

Guest: Windows 10 Professional

Hypervisor: Hyper-V

Aim:

Create a shared folder between Host and Guest via an internal network to exchange files

How can I achieve this?

答:
Share Files, Folders or Drives Between Host and Hyper-V Virtual Machine

Prerequisites

Make sure you have a Pro or Enterprise version of the Windows OS. The Home version does not provide you with this functionality! From the official documentation:

The virtual machine must have Remote Desktop Services enabled, and run Windows 10, Windows 8.1, Windows Server 2016, or Windows Server 2012 R2 as the guest operating system.

Ensure that Enhanced session mode settings are enabled on the Hyper-V host.

Start Hyper-V Manager, and in the Actions section, select "Hyper-V Settings".

Make sure that enhanced session mode is allowed in the Server section. Then, make sure that the enhanced session mode is available in the User section.

Enable Hyper-V Guest Services for your virtual machine

Right-click on Virtual Machine > Settings. Select the Integration Services in the left-lower corner of the menu. Check Guest Service and click OK.

Steps to share devices with Hyper-v virtual machine:

Start a virtual machine and click Show Options in the pop-up windows.

Or click "Edit Session Settings..." in the Actions panel on the right

It may only appear when you're (able to get) connected to it. If it doesn't appear try Starting and then Connecting to the VM while paying close attention to the panel in the Hyper-V Manager.

View local resources. Then, select the "More..." menu.

From there, you can choose which devices to share. Removable drives are especially useful for file sharing.

Choose to "Save my settings for future connections to this virtual machine".

Click Connect. Drive sharing is now complete, and you will see the shared drive in this PC > Network Locations section of Windows Explorer after using the enhanced session mode to sigh to the VM. You should now be able to copy files from a physical machine and paste them into a virtual machine, and vice versa.

Source (and for more info): Share Files, Folders or Drives Between Host and Hyper-V Virtual Machine

Drawbacks

While this solution is easy and probably works faster then when using internal network, it has its own drawback: the drive is mapped only while there is a connection via the VMConnect client. If you run a vm with some background services or you connect to it via RDP, this approach isn't gonna work. (thanks @sich for pointing this out)


问:
Angular 7 AmCharts PDF font
I'm using Angular 7 and AmCharts and I'm exporting a PDF (using the included pdfmake library), but I can't change the default font of the generated PDF. I alread created the vfs_fonts.js with the fonts I need and including it in my angular.json in the scripts section. In the component where I generate the report I do the following:

```<code>let pdfMake = res[0];

pdfMake.fonts = {
    "Open Sans": {
        "normal": 'OpenSans-Regular.ttf',
        "bold": 'OpenSans-Bold.ttf',
        "italics": 'OpenSans-Italic.ttf',
        "bolditalics": 'OpenSans-BoldItalic.ttf'
    }
}

let doc = {
    header: this.drawHeader(),
    footer: this.drawFooter(),
    pageSize: "A4",
    pageOrientation: "portrait",
    pageMargins: [40, 80, 40, 60],
    content: [],
    defaultStyle: {
        font: "Open Sans"
    }
};
</code>```

But I get the following error:

```<code>ERROR Error: Uncaught (in promise): Error: Font 'Open Sans' in style 'normal' is not defined in the font section of the document definition.
Error: Font 'Open Sans' in style 'normal' is not defined in the font section of the document definition
</code>```

How can i solve? Thank you

答:
You can use the same pdfmake library used by amcharts but you need to generate font as mentioned in the answer above by using gulp after npm install, there was promise issue so I replaced the syntax for just a quick workaround, so you have to go to the pdfmake folder located in the node_modules of you project root and run npm install after which you create fonts folder in the examples directory
-> examples/fonts and copy your ttf fonts there then run gulp file which will give you vfs_fonts.js file.

You need to edit this file and change

this.pdfMake = this.pdfMake || {}; pdfMake.vfs = {}

to

export const pdfMake={ vfs : {  }}; as in this screenshot:

and end the key appropriately like this :

 instead of this };

after that you can check my full working code with montserrat font here which i made by using the amchart custom content demo below:

```<code>import { Component, NgZone } from "@angular/core";
import * as am4core from "@amcharts/amcharts4/core";
import * as am4charts from "@amcharts/amcharts4/charts";
import am4themes_animated from "@amcharts/amcharts4/themes/animated";
import am4themes_dataviz from "@amcharts/amcharts4/themes/dataviz";

import * as pdfFontsVfs from "../../node_modules/pdfmake/build/vfs_fonts";
am4core.useTheme(am4themes_animated);

// Themes
am4core.useTheme(am4themes_animated);
am4core.useTheme(am4themes_dataviz);

@Component({
  selector: "app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})

export class AppComponent {
  private chart: am4charts.XYChart;
  private chart2: am4charts.XYChart;
  private chart3: am4charts.XYChart;
  private chart4: am4charts.PieChart;
  constructor(private zone: NgZone) {}

  ngAfterViewInit() {
    this.zone.runOutsideAngular(() => {

    /**
     * Chart 1
     */

    // Create chart instance
    var chart = am4core.create("chartdiv", am4charts.XYChart);

    // Add data
    chart.data = [{
      "date": new Date(2018, 0, 1),
      "value": 450,
      "value2": 362,
      "value3": 699
    }, {
      "date": new Date(2018, 0, 2),
      "value": 269,
      "value2": 450,
      "value3": 841
    }, {
      "date": new Date(2018, 0, 3),
      "value": 700,
      "value2": 358,
      "value3": 699
    }, {
      "date": new Date(2018, 0, 4),
      "value": 490,
      "value2": 367,
      "value3": 500
    }, {
      "date": new Date(2018, 0, 5),
      "value": 500,
      "value2": 485,
      "value3": 369
    }, {
      "date": new Date(2018, 0, 6),
      "value": 550,
      "value2": 354,
      "value3": 250
    }, {
      "date": new Date(2018, 0, 7),
      "value": 420,
      "value2": 350,
      "value3": 600
    }];

    // Create axes
    var categoryAxis = chart.xAxes.push(new am4charts.DateAxis());
    categoryAxis.renderer.grid.template.location = 0;
    categoryAxis.renderer.labels.template.disabled = true;
    categoryAxis.renderer.minGridDistance = 30;

    var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
    valueAxis.renderer.labels.template.disabled = true;

    this.createSeries("value", "Series #1",chart);
    this.createSeries("value2", "Series #2",chart);
    this.createSeries("value3", "Series #3",chart);
    this.chart = chart;

    /**
     * Chart 2
     */

    // Create chart instance
    var chart2 = am4core.create("chartdiv2", am4charts.XYChart);
    chart2.paddingBottom = 25;

    // Add data
    chart2.data = [{
      "country": "USA",
      "visits": 3025
    }, {
      "country": "China",
      "visits": 1882
    }, {
      "country": "Japan",
      "visits": 1809
    }, {
      "country": "Germany",
      "visits": 1322
    }, {
      "country": "UK",
      "visits": 1122
    }, {
      "country": "France",
      "visits": 1114
    }, {
      "country": "India",
      "visits": 984
    }];

    // Create axes
    var categoryAxis2 = chart2.xAxes.push(new am4charts.CategoryAxis());
    categoryAxis2.dataFields.category = "country";
    categoryAxis2.renderer.grid.template.location = 0;
    categoryAxis2.renderer.minGridDistance = 30;
    categoryAxis2.renderer.labels.template.disabled = true;

    var valueAxis = chart2.yAxes.push(new am4charts.ValueAxis());
    valueAxis.renderer.labels.template.disabled = true;

    // Create series
    var series = chart2.series.push(new am4charts.ColumnSeries());
    series.sequencedInterpolation = true;
    series.dataFields.valueY = "visits";
    series.dataFields.categoryX = "country";
    series.columns.template.strokeWidth = 0;

    series.columns.template.column.cornerRadiusTopLeft = 10;
    series.columns.template.column.cornerRadiusTopRight = 10;

    // on hover, make corner radiuses bigger
    var hoverState = series.columns.template.column.states.create("hover");
    hoverState.properties.cornerRadiusTopLeft = 0;
    hoverState.properties.cornerRadiusTopRight = 0;
    hoverState.properties.fillOpacity = 1;

    series.columns.template.adapter.add("fill", (fill, target)=>{
      return chart.colors.getIndex(target.dataItem.index);
    });
    this.chart2 = chart2;
    /**
     * Chart 3
     */

    // Create chart instance
    var chart3 = am4core.create("chartdiv3", am4charts.XYChart);
    chart3.paddingBottom = 25;

    // Add percent sign to all numbers
    chart3.numberFormatter.numberFormat = "#.3'%'";

    // Add data
    chart3.data = [{
        "country": "USA",
        "year2004": 3.5,
        "year2005": 4.2
    }, {
        "country": "UK",
        "year2004": 1.7,
        "year2005": 3.1
    }, {
        "country": "Canada",
        "year2004": 2.8,
        "year2005": 2.9
    }, {
        "country": "Japan",
        "year2004": 2.6,
        "year2005": 2.3
    }, {
        "country": "France",
        "year2004": 1.4,
        "year2005": 2.1
    }, {
        "country": "Brazil",
        "year2004": 2.6,
        "year2005": 4.9
    }];

    // Create axes
    var categoryAxis3 = chart3.xAxes.push(new am4charts.CategoryAxis());
    categoryAxis3.dataFields.category = "country";
    categoryAxis3.renderer.grid.template.location = 0;
    categoryAxis3.renderer.minGridDistance = 30;
    categoryAxis3.renderer.labels.template.disabled = true;

    var valueAxis3 = chart3.yAxes.push(new am4charts.ValueAxis());
    valueAxis3.renderer.labels.template.disabled = true;

    // Create series
    var series3 = chart3.series.push(new am4charts.ColumnSeries());
    series3.dataFields.valueY = "year2004";
    series3.dataFields.categoryX = "country";
    series3.clustered = false;

    series3.columns.template.column.cornerRadiusTopLeft = 10;
    series3.columns.template.column.cornerRadiusTopRight = 10;

    var series2 = chart3.series.push(new am4charts.ColumnSeries());
    series2.dataFields.valueY = "year2005";
    series2.dataFields.categoryX = "country";
    series2.clustered = false;
    series2.columns.template.width = am4core.percent(50);

    series2.columns.template.column.cornerRadiusTopLeft = 6;
    series2.columns.template.column.cornerRadiusTopRight = 6;
    this.chart3 = chart3;
    /**
     * Chart 4
     */

    // Create chart
    var chart4 = am4core.create("chartdiv4", am4charts.PieChart);
    chart4.padding(0, 0, 0, 0);

    chart4.data = [
      {
        country: "Lithuania",
        value: 260
      },
      {
        country: "Czech Republic",
        value: 230
      },
      {
        country: "Ireland",
        value: 200
      },
      {
        country: "Germany",
        value: 165
      },
      {
        country: "Australia",
        value: 139
      },
      {
        country: "Austria",
        value: 128
      }
    ];

    var series4 = chart4.series.push(new am4charts.PieSeries());
    series4.dataFields.value = "value";
    series4.dataFields.radiusValue = "value";
    series4.dataFields.category = "country";
    series4.slices.template.cornerRadius = 6;
    series4.colors.step = 3;
    series4.radius = am4core.percent(100);
    series4.labels.template.disabled = true;
    series4.ticks.template.disabled = true;
    this.chart4 = chart4;
    });
  }

  /**
   * Function that exports PDF
   */

  async savePDF() {

      var res = [];
      var fonts =  {
        "Montserrat": {
         normal: "Montserrat-Regular.ttf",
         bold: "Montserrat-Bold.ttf",
         italics: "Montserrat-Italic.ttf",
         bolditalics: "Montserrat-BoldItalic.ttf"
       }
     };
      var PdfMake = await this.chart.exporting.pdfmake;

      PdfMake.vfs = pdfFontsVfs.pdfMake.vfs;
      PdfMake.addVirtualFileSystem = pdfFontsVfs;
      PdfMake.fonts = fonts;

      res[1] = await this.chart.exporting.getImage("png");
      res[2] = await this.chart2.exporting.getImage("png");
      res[3] = await this.chart3.exporting.getImage("png");
      res[4] = await this.chart4.exporting.getImage("png");

      // pdfmake is ready

      // Create document template
      var doc = {
        pageSize: "A4",
        pageOrientation: "portrait",
        pageMargins: [30, 30, 30, 30],
        content: [],
        defaultStyle:{
          font: "Montserrat"
        }
      };

      doc.content.push({
        text: "In accumsan velit in orci tempor",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sem quam, sodales ac volutpat sed, vestibulum id quam. Sed quis arcu non elit fringilla mattis. Sed auctor mi sed efficitur vehicula. Sed bibendum odio urna, quis lobortis dui luctus ac. Duis eu lacus sodales arcu tincidunt ultrices viverra a risus. Vivamus justo massa, malesuada quis pellentesque ut, placerat in massa. Nunc bibendum diam justo, in consequat ipsum fringilla ac. Praesent porta nibh ac arcu viverra, at scelerisque neque venenatis. Donec aliquam lorem non ultrices ultrices. Aliquam efficitur eros quis tortor condimentum, id pellentesque metus iaculis. Aenean at consequat neque, a posuere lectus. In eu libero magna. Pellentesque molestie tellus nec nisi molestie, eu dignissim lacus tristique. Sed tellus nulla, suscipit a velit non, mattis dictum metus. Curabitur mi mi, convallis nec libero quis, venenatis vestibulum ante.",
        fontSize: 15,
        margin: [0, 0, 0, 15]
      });

      doc.content.push({
        text: "Aliquam lacinia justo",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        image: res[1],
        width: 530
      });

      doc.content.push({
        text: "Phasellus suscipit in diam a interdum",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        table: {
          headerRows: 1,
          widths: [ "*", "*", "*", "*" ],
          body: [
            [
              { text: "USA", bold: true },
              { text: "Japan", bold: true },
              { text: "France", bold: true },
              { text: "Mexico", bold: true }
            ],
            [ "2500", "2500", "2200", "1200" ],
            [ "800", "1200", "990", "708" ],
            [ "2100", "2150", "900", "1260" ],
          ]
        }
      });

      doc.content.push({
        text: "Duis sed efficitur mauris",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        columns: [{
          image: res[2],
          width: 250
        }, {
          image: res[3],
          width: 250
        }],
        columnGap: 30
      });

      doc.content.push({
        text: "Aliquam semper lacinia",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        columns: [{
          image: res[4],
          width: 150
        }, {
          stack: [{
            text: "Maecenas congue leo vel tortor faucibus, non semper odio viverra. In ac libero rutrum libero elementum blandit vel in orci. Donec sit amet nisl ac eros mollis molestie. Curabitur ut urna vitae turpis bibendum malesuada sit amet imperdiet orci. Etiam pulvinar quam at lorem pellentesque congue. Integer sed odio enim. Maecenas eu nulla justo. Sed quis enim in est sodales facilisis non sed erat. Aenean vel ornare urna. Praesent viverra volutpat ex a aliquet.",
            fontSize: 15,
            margin: [0, 0, 0, 15]
          }, {
       text: "Fusce sed quam pharetra, ornare ligula id, maximus risus. Integer dignissim risus in placerat mattis. Fusce malesuada dui ut lectus ultricies, et sollicitudin nisl placerat. In dignissim elit in pretium lobortis. Fusce ornare enim at metus laoreet, ut convallis elit lacinia. Maecenas pharetra aliquet mi. Nulla orci nunc, egestas id nisi ut, volutpat sollicitudin mi.",
        fontSize: 15,
        margin: [0, 0, 0, 15]
      }],
      width: "*"
    }],
    columnGap: 30
  });

    await  PdfMake.createPdf(doc,null, fonts,pdfFontsVfs.pdfMake.vfs).download("report.pdf");

  }

  // Create series
  createSeries(field, name, chart) {
    console.log(chart);
    var series = chart.series.push(new am4charts.LineSeries());
    series.dataFields.valueY = field;
    series.dataFields.dateX = "date";
    series.name = name;
    series.tooltipText = "{dateX}: [b]{valueY}[/]";
    series.strokeWidth = 3;

    var bullet = series.bullets.push(new am4charts.CircleBullet());
    bullet.circle.stroke = am4core.color("#fff");
    bullet.circle.strokeWidth = 3;
    bullet.circle.radius = 7;
  }
  ngOnDestroy() {
    this.zone.runOutsideAngular(() => {
      if (this.chart) {
        this.chart.dispose();
        ...dispose other charts
      }
    });
  }
}
</code>```

and webpack config is:

```<code>const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/main.ts',
    resolve: {
        extensions: ['.ts', '.js']
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: ['ts-loader', 'angular2-template-loader'],
                exclude: [ /node_modules/, /pdfmake.js$/ ]
            },
            {
                test: /\.(html|css)$/,
                use: 'raw-loader'
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({ template: './src/index.html' })
    ],

}
</code>```

and html is:

```<code>    <div class="main">

  <input type="button" value="Save as PDF" (click)="savePDF()" />

<h1>In accumsan velit in orci tempor</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sem quam, sodales ac volutpat sed, vestibulum id quam. Sed quis arcu non elit fringilla mattis. Sed auctor mi sed efficitur vehicula. Sed bibendum odio urna, quis lobortis dui luctus ac. Duis eu lacus sodales arcu tincidunt ultrices viverra a risus. Vivamus justo massa, malesuada quis pellentesque ut, placerat in massa. Nunc bibendum diam justo, in consequat ipsum fringilla ac. Praesent porta nibh ac arcu viverra, at scelerisque neque venenatis. Donec aliquam lorem non ultrices ultrices. Aliquam efficitur eros quis tortor condimentum, id pellentesque metus iaculis. Aenean at consequat neque, a posuere lectus. In eu libero magna. Pellentesque molestie tellus nec nisi molestie, eu dignissim lacus tristique. Sed tellus nulla, suscipit a velit non, mattis dictum metus. Curabitur mi mi, convallis nec libero quis, venenatis vestibulum ante.</p>
<h2>Aliquam lacinia justo</h2>
<div id="chartdiv" class="chart"></div>

<h2>Phasellus suscipit in diam a interdum</h2>
<table>
  <tr>
    <th>USA</th>
    <th>Japan</th>
    <th>France</th>
    <th>Mexico</th>
  </tr>
  <tr>
    <td>2500</td>
    <td>1900</td>
    <td>2200</td>
    <td>1200</td>
  </tr>
  <tr>
    <td>800</td>
    <td>1200</td>
    <td>990</td>
    <td>708</td>
  </tr>
  <tr>
    <td>2100</td>
    <td>2150</td>
    <td>900</td>
    <td>1260</td>
  </tr>
</table>

<h2>Duis sed efficitur mauris</h2>
<div>
  <div class="col">
    <div id="chartdiv2" class="chart"></div>
  </div>
  <div class="col">
    <div id="chartdiv3" class="chart"></div>
  </div>
</div>

<br>
<h2>Aliquam semper lacinia</h2>
<div id="chartdiv4" class="chart"></div>
<p>Maecenas congue leo vel tortor faucibus, non semper odio viverra. In ac libero rutrum libero elementum blandit vel in orci. Donec sit amet nisl ac eros mollis molestie. Curabitur ut urna vitae turpis bibendum malesuada sit amet imperdiet orci. Etiam pulvinar quam at lorem pellentesque congue. Integer sed odio enim. Maecenas eu nulla justo. Sed quis enim in est sodales facilisis non sed erat. Aenean vel ornare urna. Praesent viverra volutpat ex a aliquet.</p>

<p>Fusce sed quam pharetra, ornare ligula id, maximus risus. Integer dignissim risus in placerat mattis. Fusce malesuada dui ut lectus ultricies, et sollicitudin nisl placerat. In dignissim elit in pretium lobortis. Fusce ornare enim at metus laoreet, ut convallis elit lacinia. Maecenas pharetra aliquet mi. Nulla orci nunc, egestas id nisi ut, volutpat sollicitudin mi.</p>

</div>
</code>```

and package.json is :

```<code>    {
    "name": "angular-7-tutorial",
    "version": "1.0.0",
    "scripts": {
        "start": "webpack-dev-server --mode development --open"
    },
    "dependencies": {
        "@amcharts/amcharts4": "^4.9.1",
        "@angular/common": "^7.2.13",
        "@angular/compiler": "^7.2.13",
        "@angular/core": "^7.2.13",
        "@angular/forms": "^7.2.13",
        "@angular/platform-browser": "^7.2.13",
        "@angular/platform-browser-dynamic": "^7.2.13",
        "@angular/router": "^7.2.13",
        "core-js": "^3.0.1",
        "rxjs": "^6.4.0",
        "zone.js": "^0.9.0"
    },
    "devDependencies": {
        "@types/node": "^11.13.5",
        "angular2-template-loader": "^0.6.2",
        "html-webpack-plugin": "^3.2.0",
        "raw-loader": "^1.0.0",
        "ts-loader": "^5.3.3",
        "typescript": "^3.4.4",
        "webpack": "^4.30.0",
        "webpack-cli": "^3.3.0",
        "webpack-dev-server": "^3.3.1"
    }
}
</code>```

and folder structure:

The thing I understood is syntax conflict and promise unresolved failure preventing the charts pdf not loading the custom fonts. If you see the default amcharts vs_fonts file , you will see export default syntax which might caused the issue. Secondly check your font files if they are not corrupt.
Hope this will help you. Thanks.

Title: Angular 7 AmCharts PDF font

Body:
I'm using Angular 7 and AmCharts and I'm exporting a PDF (using the included pdfmake library), but I can't change the default font of the generated PDF. I alread created the vfs_fonts.js with the fonts I need and including it in my angular.json in the scripts section. In the component where I generate the report I do the following:

let pdfMake = res[0];

pdfMake.fonts = {
    "Open Sans": {
        "normal": 'OpenSans-Regular.ttf',
        "bold": 'OpenSans-Bold.ttf',
        "italics": 'OpenSans-Italic.ttf',
        "bolditalics": 'OpenSans-BoldItalic.ttf'
    }
}

let doc = {
    header: this.drawHeader(),
    footer: this.drawFooter(),
    pageSize: "A4",
    pageOrientation: "portrait",
    pageMargins: [40, 80, 40, 60],
    content: [],
    defaultStyle: {
        font: "Open Sans"
    }
};

But I get the following error:

ERROR Error: Uncaught (in promise): Error: Font 'Open Sans' in style 'normal' is not defined in the font section of the document definition.
Error: Font 'Open Sans' in style 'normal' is not defined in the font section of the document definition

How can i solve? Thank you

Body:
You can use the same pdfmake library used by amcharts but you need to generate font as mentioned in the answer above by using gulp after npm install, there was promise issue so I replaced the syntax for just a quick workaround, so you have to go to the pdfmake folder located in the node_modules of you project root and run npm install after which you create fonts folder in the examples directory
-> examples/fonts and copy your ttf fonts there then run gulp file which will give you vfs_fonts.js file.

You need to edit this file and change

this.pdfMake = this.pdfMake || {}; pdfMake.vfs = {}

to

export const pdfMake={ vfs : {  }}; as in this screenshot:

and end the key appropriately like this :

 instead of this };

after that you can check my full working code with montserrat font here which i made by using the amchart custom content demo below:

import { Component, NgZone } from "@angular/core";
import * as am4core from "@amcharts/amcharts4/core";
import * as am4charts from "@amcharts/amcharts4/charts";
import am4themes_animated from "@amcharts/amcharts4/themes/animated";
import am4themes_dataviz from "@amcharts/amcharts4/themes/dataviz";

import * as pdfFontsVfs from "../../node_modules/pdfmake/build/vfs_fonts";
am4core.useTheme(am4themes_animated);

// Themes
am4core.useTheme(am4themes_animated);
am4core.useTheme(am4themes_dataviz);

@Component({
  selector: "app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})

export class AppComponent {
  private chart: am4charts.XYChart;
  private chart2: am4charts.XYChart;
  private chart3: am4charts.XYChart;
  private chart4: am4charts.PieChart;
  constructor(private zone: NgZone) {}

  ngAfterViewInit() {
    this.zone.runOutsideAngular(() => {

    /**
     * Chart 1
     */

    // Create chart instance
    var chart = am4core.create("chartdiv", am4charts.XYChart);

    // Add data
    chart.data = [{
      "date": new Date(2018, 0, 1),
      "value": 450,
      "value2": 362,
      "value3": 699
    }, {
      "date": new Date(2018, 0, 2),
      "value": 269,
      "value2": 450,
      "value3": 841
    }, {
      "date": new Date(2018, 0, 3),
      "value": 700,
      "value2": 358,
      "value3": 699
    }, {
      "date": new Date(2018, 0, 4),
      "value": 490,
      "value2": 367,
      "value3": 500
    }, {
      "date": new Date(2018, 0, 5),
      "value": 500,
      "value2": 485,
      "value3": 369
    }, {
      "date": new Date(2018, 0, 6),
      "value": 550,
      "value2": 354,
      "value3": 250
    }, {
      "date": new Date(2018, 0, 7),
      "value": 420,
      "value2": 350,
      "value3": 600
    }];

    // Create axes
    var categoryAxis = chart.xAxes.push(new am4charts.DateAxis());
    categoryAxis.renderer.grid.template.location = 0;
    categoryAxis.renderer.labels.template.disabled = true;
    categoryAxis.renderer.minGridDistance = 30;

    var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
    valueAxis.renderer.labels.template.disabled = true;

    this.createSeries("value", "Series #1",chart);
    this.createSeries("value2", "Series #2",chart);
    this.createSeries("value3", "Series #3",chart);
    this.chart = chart;

    /**
     * Chart 2
     */

    // Create chart instance
    var chart2 = am4core.create("chartdiv2", am4charts.XYChart);
    chart2.paddingBottom = 25;

    // Add data
    chart2.data = [{
      "country": "USA",
      "visits": 3025
    }, {
      "country": "China",
      "visits": 1882
    }, {
      "country": "Japan",
      "visits": 1809
    }, {
      "country": "Germany",
      "visits": 1322
    }, {
      "country": "UK",
      "visits": 1122
    }, {
      "country": "France",
      "visits": 1114
    }, {
      "country": "India",
      "visits": 984
    }];

    // Create axes
    var categoryAxis2 = chart2.xAxes.push(new am4charts.CategoryAxis());
    categoryAxis2.dataFields.category = "country";
    categoryAxis2.renderer.grid.template.location = 0;
    categoryAxis2.renderer.minGridDistance = 30;
    categoryAxis2.renderer.labels.template.disabled = true;

    var valueAxis = chart2.yAxes.push(new am4charts.ValueAxis());
    valueAxis.renderer.labels.template.disabled = true;

    // Create series
    var series = chart2.series.push(new am4charts.ColumnSeries());
    series.sequencedInterpolation = true;
    series.dataFields.valueY = "visits";
    series.dataFields.categoryX = "country";
    series.columns.template.strokeWidth = 0;

    series.columns.template.column.cornerRadiusTopLeft = 10;
    series.columns.template.column.cornerRadiusTopRight = 10;

    // on hover, make corner radiuses bigger
    var hoverState = series.columns.template.column.states.create("hover");
    hoverState.properties.cornerRadiusTopLeft = 0;
    hoverState.properties.cornerRadiusTopRight = 0;
    hoverState.properties.fillOpacity = 1;

    series.columns.template.adapter.add("fill", (fill, target)=>{
      return chart.colors.getIndex(target.dataItem.index);
    });
    this.chart2 = chart2;
    /**
     * Chart 3
     */

    // Create chart instance
    var chart3 = am4core.create("chartdiv3", am4charts.XYChart);
    chart3.paddingBottom = 25;

    // Add percent sign to all numbers
    chart3.numberFormatter.numberFormat = "#.3'%'";

    // Add data
    chart3.data = [{
        "country": "USA",
        "year2004": 3.5,
        "year2005": 4.2
    }, {
        "country": "UK",
        "year2004": 1.7,
        "year2005": 3.1
    }, {
        "country": "Canada",
        "year2004": 2.8,
        "year2005": 2.9
    }, {
        "country": "Japan",
        "year2004": 2.6,
        "year2005": 2.3
    }, {
        "country": "France",
        "year2004": 1.4,
        "year2005": 2.1
    }, {
        "country": "Brazil",
        "year2004": 2.6,
        "year2005": 4.9
    }];

    // Create axes
    var categoryAxis3 = chart3.xAxes.push(new am4charts.CategoryAxis());
    categoryAxis3.dataFields.category = "country";
    categoryAxis3.renderer.grid.template.location = 0;
    categoryAxis3.renderer.minGridDistance = 30;
    categoryAxis3.renderer.labels.template.disabled = true;

    var valueAxis3 = chart3.yAxes.push(new am4charts.ValueAxis());
    valueAxis3.renderer.labels.template.disabled = true;

    // Create series
    var series3 = chart3.series.push(new am4charts.ColumnSeries());
    series3.dataFields.valueY = "year2004";
    series3.dataFields.categoryX = "country";
    series3.clustered = false;

    series3.columns.template.column.cornerRadiusTopLeft = 10;
    series3.columns.template.column.cornerRadiusTopRight = 10;

    var series2 = chart3.series.push(new am4charts.ColumnSeries());
    series2.dataFields.valueY = "year2005";
    series2.dataFields.categoryX = "country";
    series2.clustered = false;
    series2.columns.template.width = am4core.percent(50);

    series2.columns.template.column.cornerRadiusTopLeft = 6;
    series2.columns.template.column.cornerRadiusTopRight = 6;
    this.chart3 = chart3;
    /**
     * Chart 4
     */

    // Create chart
    var chart4 = am4core.create("chartdiv4", am4charts.PieChart);
    chart4.padding(0, 0, 0, 0);

    chart4.data = [
      {
        country: "Lithuania",
        value: 260
      },
      {
        country: "Czech Republic",
        value: 230
      },
      {
        country: "Ireland",
        value: 200
      },
      {
        country: "Germany",
        value: 165
      },
      {
        country: "Australia",
        value: 139
      },
      {
        country: "Austria",
        value: 128
      }
    ];

    var series4 = chart4.series.push(new am4charts.PieSeries());
    series4.dataFields.value = "value";
    series4.dataFields.radiusValue = "value";
    series4.dataFields.category = "country";
    series4.slices.template.cornerRadius = 6;
    series4.colors.step = 3;
    series4.radius = am4core.percent(100);
    series4.labels.template.disabled = true;
    series4.ticks.template.disabled = true;
    this.chart4 = chart4;
    });
  }

  /**
   * Function that exports PDF
   */

  async savePDF() {

      var res = [];
      var fonts =  {
        "Montserrat": {
         normal: "Montserrat-Regular.ttf",
         bold: "Montserrat-Bold.ttf",
         italics: "Montserrat-Italic.ttf",
         bolditalics: "Montserrat-BoldItalic.ttf"
       }
     };
      var PdfMake = await this.chart.exporting.pdfmake;

      PdfMake.vfs = pdfFontsVfs.pdfMake.vfs;
      PdfMake.addVirtualFileSystem = pdfFontsVfs;
      PdfMake.fonts = fonts;

      res[1] = await this.chart.exporting.getImage("png");
      res[2] = await this.chart2.exporting.getImage("png");
      res[3] = await this.chart3.exporting.getImage("png");
      res[4] = await this.chart4.exporting.getImage("png");

      // pdfmake is ready

      // Create document template
      var doc = {
        pageSize: "A4",
        pageOrientation: "portrait",
        pageMargins: [30, 30, 30, 30],
        content: [],
        defaultStyle:{
          font: "Montserrat"
        }
      };

      doc.content.push({
        text: "In accumsan velit in orci tempor",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sem quam, sodales ac volutpat sed, vestibulum id quam. Sed quis arcu non elit fringilla mattis. Sed auctor mi sed efficitur vehicula. Sed bibendum odio urna, quis lobortis dui luctus ac. Duis eu lacus sodales arcu tincidunt ultrices viverra a risus. Vivamus justo massa, malesuada quis pellentesque ut, placerat in massa. Nunc bibendum diam justo, in consequat ipsum fringilla ac. Praesent porta nibh ac arcu viverra, at scelerisque neque venenatis. Donec aliquam lorem non ultrices ultrices. Aliquam efficitur eros quis tortor condimentum, id pellentesque metus iaculis. Aenean at consequat neque, a posuere lectus. In eu libero magna. Pellentesque molestie tellus nec nisi molestie, eu dignissim lacus tristique. Sed tellus nulla, suscipit a velit non, mattis dictum metus. Curabitur mi mi, convallis nec libero quis, venenatis vestibulum ante.",
        fontSize: 15,
        margin: [0, 0, 0, 15]
      });

      doc.content.push({
        text: "Aliquam lacinia justo",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        image: res[1],
        width: 530
      });

      doc.content.push({
        text: "Phasellus suscipit in diam a interdum",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        table: {
          headerRows: 1,
          widths: [ "*", "*", "*", "*" ],
          body: [
            [
              { text: "USA", bold: true },
              { text: "Japan", bold: true },
              { text: "France", bold: true },
              { text: "Mexico", bold: true }
            ],
            [ "2500", "2500", "2200", "1200" ],
            [ "800", "1200", "990", "708" ],
            [ "2100", "2150", "900", "1260" ],
          ]
        }
      });

      doc.content.push({
        text: "Duis sed efficitur mauris",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        columns: [{
          image: res[2],
          width: 250
        }, {
          image: res[3],
          width: 250
        }],
        columnGap: 30
      });

      doc.content.push({
        text: "Aliquam semper lacinia",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        columns: [{
          image: res[4],
          width: 150
        }, {
          stack: [{
            text: "Maecenas congue leo vel tortor faucibus, non semper odio viverra. In ac libero rutrum libero elementum blandit vel in orci. Donec sit amet nisl ac eros mollis molestie. Curabitur ut urna vitae turpis bibendum malesuada sit amet imperdiet orci. Etiam pulvinar quam at lorem pellentesque congue. Integer sed odio enim. Maecenas eu nulla justo. Sed quis enim in est sodales facilisis non sed erat. Aenean vel ornare urna. Praesent viverra volutpat ex a aliquet.",
            fontSize: 15,
            margin: [0, 0, 0, 15]
          }, {
       text: "Fusce sed quam pharetra, ornare ligula id, maximus risus. Integer dignissim risus in placerat mattis. Fusce malesuada dui ut lectus ultricies, et sollicitudin nisl placerat. In dignissim elit in pretium lobortis. Fusce ornare enim at metus laoreet, ut convallis elit lacinia. Maecenas pharetra aliquet mi. Nulla orci nunc, egestas id nisi ut, volutpat sollicitudin mi.",
        fontSize: 15,
        margin: [0, 0, 0, 15]
      }],
      width: "*"
    }],
    columnGap: 30
  });

    await  PdfMake.createPdf(doc,null, fonts,pdfFontsVfs.pdfMake.vfs).download("report.pdf");

  }

  // Create series
  createSeries(field, name, chart) {
    console.log(chart);
    var series = chart.series.push(new am4charts.LineSeries());
    series.dataFields.valueY = field;
    series.dataFields.dateX = "date";
    series.name = name;
    series.tooltipText = "{dateX}: [b]{valueY}[/]";
    series.strokeWidth = 3;

    var bullet = series.bullets.push(new am4charts.CircleBullet());
    bullet.circle.stroke = am4core.color("#fff");
    bullet.circle.strokeWidth = 3;
    bullet.circle.radius = 7;
  }
  ngOnDestroy() {
    this.zone.runOutsideAngular(() => {
      if (this.chart) {
        this.chart.dispose();
        ...dispose other charts
      }
    });
  }
}

and webpack config is:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/main.ts',
    resolve: {
        extensions: ['.ts', '.js']
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: ['ts-loader', 'angular2-template-loader'],
                exclude: [ /node_modules/, /pdfmake.js$/ ]
            },
            {
                test: /\.(html|css)$/,
                use: 'raw-loader'
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({ template: './src/index.html' })
    ],

}

and html is:

    <div class="main">

  <input type="button" value="Save as PDF" (click)="savePDF()" />

<h1>In accumsan velit in orci tempor</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sem quam, sodales ac volutpat sed, vestibulum id quam. Sed quis arcu non elit fringilla mattis. Sed auctor mi sed efficitur vehicula. Sed bibendum odio urna, quis lobortis dui luctus ac. Duis eu lacus sodales arcu tincidunt ultrices viverra a risus. Vivamus justo massa, malesuada quis pellentesque ut, placerat in massa. Nunc bibendum diam justo, in consequat ipsum fringilla ac. Praesent porta nibh ac arcu viverra, at scelerisque neque venenatis. Donec aliquam lorem non ultrices ultrices. Aliquam efficitur eros quis tortor condimentum, id pellentesque metus iaculis. Aenean at consequat neque, a posuere lectus. In eu libero magna. Pellentesque molestie tellus nec nisi molestie, eu dignissim lacus tristique. Sed tellus nulla, suscipit a velit non, mattis dictum metus. Curabitur mi mi, convallis nec libero quis, venenatis vestibulum ante.</p>
<h2>Aliquam lacinia justo</h2>
<div id="chartdiv" class="chart"></div>

<h2>Phasellus suscipit in diam a interdum</h2>
<table>
  <tr>
    <th>USA</th>
    <th>Japan</th>
    <th>France</th>
    <th>Mexico</th>
  </tr>
  <tr>
    <td>2500</td>
    <td>1900</td>
    <td>2200</td>
    <td>1200</td>
  </tr>
  <tr>
    <td>800</td>
    <td>1200</td>
    <td>990</td>
    <td>708</td>
  </tr>
  <tr>
    <td>2100</td>
    <td>2150</td>
    <td>900</td>
    <td>1260</td>
  </tr>
</table>

<h2>Duis sed efficitur mauris</h2>
<div>
  <div class="col">
    <div id="chartdiv2" class="chart"></div>
  </div>
  <div class="col">
    <div id="chartdiv3" class="chart"></div>
  </div>
</div>

<br>
<h2>Aliquam semper lacinia</h2>
<div id="chartdiv4" class="chart"></div>
<p>Maecenas congue leo vel tortor faucibus, non semper odio viverra. In ac libero rutrum libero elementum blandit vel in orci. Donec sit amet nisl ac eros mollis molestie. Curabitur ut urna vitae turpis bibendum malesuada sit amet imperdiet orci. Etiam pulvinar quam at lorem pellentesque congue. Integer sed odio enim. Maecenas eu nulla justo. Sed quis enim in est sodales facilisis non sed erat. Aenean vel ornare urna. Praesent viverra volutpat ex a aliquet.</p>

<p>Fusce sed quam pharetra, ornare ligula id, maximus risus. Integer dignissim risus in placerat mattis. Fusce malesuada dui ut lectus ultricies, et sollicitudin nisl placerat. In dignissim elit in pretium lobortis. Fusce ornare enim at metus laoreet, ut convallis elit lacinia. Maecenas pharetra aliquet mi. Nulla orci nunc, egestas id nisi ut, volutpat sollicitudin mi.</p>

</div>

and package.json is :

    {
    "name": "angular-7-tutorial",
    "version": "1.0.0",
    "scripts": {
        "start": "webpack-dev-server --mode development --open"
    },
    "dependencies": {
        "@amcharts/amcharts4": "^4.9.1",
        "@angular/common": "^7.2.13",
        "@angular/compiler": "^7.2.13",
        "@angular/core": "^7.2.13",
        "@angular/forms": "^7.2.13",
        "@angular/platform-browser": "^7.2.13",
        "@angular/platform-browser-dynamic": "^7.2.13",
        "@angular/router": "^7.2.13",
        "core-js": "^3.0.1",
        "rxjs": "^6.4.0",
        "zone.js": "^0.9.0"
    },
    "devDependencies": {
        "@types/node": "^11.13.5",
        "angular2-template-loader": "^0.6.2",
        "html-webpack-plugin": "^3.2.0",
        "raw-loader": "^1.0.0",
        "ts-loader": "^5.3.3",
        "typescript": "^3.4.4",
        "webpack": "^4.30.0",
        "webpack-cli": "^3.3.0",
        "webpack-dev-server": "^3.3.1"
    }
}

and folder structure:

The thing I understood is syntax conflict and promise unresolved failure preventing the charts pdf not loading the custom fonts. If you see the default amcharts vs_fonts file , you will see export default syntax which might caused the issue. Secondly check your font files if they are not corrupt.
Hope this will help you. Thanks.


问:
Ordering Grep Output With Multiple Patterns
The file I'm searching (fruit.text) looks something like the below snippet, the data appears in random order that I cannot control.

....fruit=apple,...qty=3,...condition=bad,....

...qty=4,...condition=great,...fruit=orange,...

...condition=ok,...qty=2,...fruit=banana,...

My Grep command is: grep -Eo 'fruit.[^,]*'\|'qty.[^,]*'\|'condition.[^,]*' fruit.txt

This results in output like:

fruit=apple

qty=3

condition=bad

qty=4

condition=great

fruit=orange

condition=ok

qty=2

fruit=banana

Which is correct, however, I'm looking for the output to be ordered as I specified in the grep cmd. ie, exactly like the below:

fruit=apple

qty=3

condition=bad

fruit=orange

qty=4

condition=great

fruit=banana

qty=2

condition=ok

答:
Using sed in some steps:

```<code>sed -E 's/^/,/;
        s/(.*),(condition[^,]*)/\2\r,\1/;
        s/(.*),(qty=[^,]*)/\2,\1/;
        s/(.*),(fruit=[^,]*)/\2,\1/;
        s/\r.*//;
        s/,/\n/g' input.txt
</code>```

I start with inserting a , for input where the interesting data starts in the first field.

After condition I add a \r, so I can remove the garbage after finding the fruit.

Body:
Using sed in some steps:

sed -E 's/^/,/;
        s/(.*),(condition[^,]*)/\2\r,\1/;
        s/(.*),(qty=[^,]*)/\2,\1/;
        s/(.*),(fruit=[^,]*)/\2,\1/;
        s/\r.*//;
        s/,/\n/g' input.txt

I start with inserting a , for input where the interesting data starts in the first field.

After condition I add a \r, so I can remove the garbage after finding the fruit.


问:
array_walk not changing value
```<code>function values($id,$col)
{
     $vals = [1=>['name'=>'Lifting Heavy Boxes']];
     return $vals[$id][$col];
}
$complete = [1=>["id"=>"2","sid"=>"35","material_completed"=>"1","date"=>"2017-12-18"]];
$form = 'my_form';

array_walk($complete, function(&$d,$k) use($form) {
    $k = values($k, 'name').' ['.date('m/d/y',strtotime($d['date'])).'] ('.$form.')';
    echo 'in walk '.$k."\n";
});
print_r($complete);
</code>```

the echo outputs:

in walk Lifting Heavy Boxes [12/18/17] (my_form)

the print_r outputs:

```<code>Array
(
    [1] => Array
        (
            [id] => 2
            [sid] => 35
            [material_completed] => 1
            [date] => 2017-12-18
        )

)
</code>```

I have another array walk that is very similar that is doing just fine. The only difference I can perceive between them is in the one that's working, the value $d is already a string before it goes through the walk, whereas in the one that's not working, $d is an array that is converted to a string inside the walk (successfully, but ultimately unsuccessfully).

Something I'm missing?

And here's the fixed version:

```<code>array_walk($complete, function(&$d,$k) use($form) {
    $d = values($k, 'name').' ['.date('m/d/y',strtotime($d['date'])).'] ('.$form.')';
});
</code>```

That's what I was trying to do anyway. I wasn't trying to change the key. I was under the mistaken impression that to change the value you had to set the key to the new value.

答:
You cannot change the key of the array inside the callback of array_walk():

Only the values of the array may potentially be changed; its structure cannot be altered, i.e., the programmer cannot add, unset or reorder elements. If the callback does not respect this requirement, the behavior of this function is undefined, and unpredictable.

This is also mentioned in the first comment:

It's worth nothing that array_walk can not be used to change keys in the array.
  The function may be defined as (&$value, $key) but not (&$value, &$key).
  Even though PHP does not complain/warn, it does not modify the key.

Title: array_walk not changing value

Body:
function values($id,$col)
{
     $vals = [1=>['name'=>'Lifting Heavy Boxes']];
     return $vals[$id][$col];
}
$complete = [1=>["id"=>"2","sid"=>"35","material_completed"=>"1","date"=>"2017-12-18"]];
$form = 'my_form';

array_walk($complete, function(&$d,$k) use($form) {
    $k = values($k, 'name').' ['.date('m/d/y',strtotime($d['date'])).'] ('.$form.')';
    echo 'in walk '.$k."\n";
});
print_r($complete);

the echo outputs:

in walk Lifting Heavy Boxes [12/18/17] (my_form)

the print_r outputs:

Array
(
    [1] => Array
        (
            [id] => 2
            [sid] => 35
            [material_completed] => 1
            [date] => 2017-12-18
        )

)

I have another array walk that is very similar that is doing just fine. The only difference I can perceive between them is in the one that's working, the value $d is already a string before it goes through the walk, whereas in the one that's not working, $d is an array that is converted to a string inside the walk (successfully, but ultimately unsuccessfully).

Something I'm missing?

And here's the fixed version:

array_walk($complete, function(&$d,$k) use($form) {
    $d = values($k, 'name').' ['.date('m/d/y',strtotime($d['date'])).'] ('.$form.')';
});

That's what I was trying to do anyway. I wasn't trying to change the key. I was under the mistaken impression that to change the value you had to set the key to the new value.


问:
Using PHP to check for mysql DB changes
I'm creating a new web application and I only want to update certain tables when the db is changed, this will be done by firing off a PHP script in a JavaScript setInterval.

My issue is that I am having a hard time trying to find a way to easily determine if certain rows in the database table have changed. In other instances I have used "show table status" and compared that against the last update_time that I had already stored.

In this case though, I don't want to look at the entire table, I just need to know if the rows in my query have changed. I originally tried to md5 the array, as well as md5 on the serialization of the array. It seems that neither of them work and return the same md5 hash each time.

Just for example, here is my query:

```<code>$getUserTable = mysql_query("select * from uct_admin.resources where teamID='62' order by ResourceName");
</code>```

Solution that I came up with after reading the answers:

```<code>$getUserTable = mysql_query("select * from uct_admin.resources where teamID='62' order by csiname");
while ($row = mysql_fetch_assoc($getUserTable)) {
    $arrayTest[] = $row;
}
$hash = md5(http_build_query($arrayTest, 'flags_'));
</code>```

This works perfectly due to the nested array returned from the query.

答:
(I'd go with James' solution unless there's a specific reason you can't)

I did something similar and managed to get the hashing solution you alluded to working okay. It would be very strange to get the same hash from different data.

In my code I'm doing an implode on the array rather than a serialization. I then store the hash in the database and when I want to compare a new set of data against it I run the md5 on the new data and compare it to the hash.

If it still doesn't work can we maybe see your code for hashing and some sample data? I'd be curious to see what's up if this method doesn't work.

Title: Using PHP to check for mysql DB changes

Body:
I'm creating a new web application and I only want to update certain tables when the db is changed, this will be done by firing off a PHP script in a JavaScript setInterval.

My issue is that I am having a hard time trying to find a way to easily determine if certain rows in the database table have changed. In other instances I have used "show table status" and compared that against the last update_time that I had already stored.

In this case though, I don't want to look at the entire table, I just need to know if the rows in my query have changed. I originally tried to md5 the array, as well as md5 on the serialization of the array. It seems that neither of them work and return the same md5 hash each time.

Just for example, here is my query:

$getUserTable = mysql_query("select * from uct_admin.resources where teamID='62' order by ResourceName");

Solution that I came up with after reading the answers:

$getUserTable = mysql_query("select * from uct_admin.resources where teamID='62' order by csiname");
while ($row = mysql_fetch_assoc($getUserTable)) {
    $arrayTest[] = $row;
}
$hash = md5(http_build_query($arrayTest, 'flags_'));

This works perfectly due to the nested array returned from the query.


问:
Optimize combinational MongoDB query in Node.js
I have ten stations stored in the stations collection: Station A, Station B, Station C, Station D, Station E, Station F, Station G, Station H, Station I, Station J.

Right now, to create a count list of all inter-station rides between all possible pairs of stations, I do the following in my Node.js code (using Mongoose):

const stationCombinations = []

// get all stations from the stations collection
const stationIds = await Station.find({}, '_id name').lean().exec()

// list of all possible from & to combinations with their names
stationIds.forEach(fromStation => {
  stationIds.forEach(toStation => {
    stationCombinations.push({ fromStation, toStation })
  })
})

const results = []

// loop through all station combinations
for (const stationCombination of stationCombinations) {
  // create aggregation query promise
  const data = Ride.aggregate([
    {
      $match: {
        test: false,
        state: 'completed',
        duration: { $gt: 2 },
        fromStation: mongoose.Types.ObjectId(stationCombination.fromStation._id),
        toStation: mongoose.Types.ObjectId(stationCombination.toStation._id)
       }
    },
    {
      $group: {
        _id: null,
        count: { $sum: 1 }
      }
    },
    {
      $addFields: {
        fromStation: stationCombination.fromStation.name,
        toStation: stationCombination.toStation.name
      }
    }
  ])

  // push promise to array
  results.push(data)
}

// run all aggregation queries
const stationData = await Promise.all(results)

// flatten nested/empty arrays and return
return stationData.flat()
```

<p>Executing this function give me the result in this format:</p>

<pre class="lang-js prettyprint-override"><code>[
  {
    "fromStation": "Station A",
    "toStation": "Station A",
    "count": 1196
  },
  {
    "fromStation": "Station A",
    "toStation": "Station B",
    "count": 1
  },
  {
    "fromStation": "Station A",
    "toStation": "Station C",
    "count": 173
  },
]

And so on for all other combinations...
</code>```

The query currently takes a lot of time to execute and I keep getting alerts from MongoDB Atlas about excessive load on the database server because of these queries. Surely there must be an optimized way to do something like this?

答:
You need to use MongoDB native operations. You need to $group by fromStation and toStation and with $lookup join two collections.

Note: I assume you have MongoDB >=v3.6 and Station._id is ObjectId

```<code>db.ride.aggregate([
  {
    $match: {
      test: false,
      state: "completed",
      duration: {
        $gt: 2
      }
    }
  },
  {
    $group: {
      _id: {
        fromStation: "$fromStation",
        toStation: "$toStation"
      },
      count: {
        $sum: 1
      }
    }
  },
  {
    $lookup: {
      from: "station",
      let: {
        fromStation: "$_id.fromStation",
        toStation: "$_id.toStation"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $in: [
                "$_id",
                [
                  "$$fromStation",
                  "$$toStation"
                ]
              ]
            }
          }
        }
      ],
      as: "tmp"
    }
  },
  {
    $project: {
      _id: 0,
      fromStation: {
        $reduce: {
          input: "$tmp",
          initialValue: "",
          in: {
            $cond: [
              {
                $eq: [
                  "$_id.fromStation",
                  "$$this._id"
                ]
              },
              "$$this.name",
              "$$value"
            ]
          }
        }
      },
      toStation: {
        $reduce: {
          input: "$tmp",
          initialValue: "",
          in: {
            $cond: [
              {
                $eq: [
                  "$_id.toStation",
                  "$$this._id"
                ]
              },
              "$$this.name",
              "$$value"
            ]
          }
        }
      },
      count: 1
    }
  },
  {
    $sort: {
      fromStation: 1,
      toStation: 1
    }
  }
])
</code>```

MongoPlayground

Not tested:

```<code>const data = Ride.aggregate([
  {
     $match: {
       test: false,
       state: 'completed',
       duration: { $gt: 2 }
     }
  },
  {
    $group: {
      _id: {
        fromStation: "$fromStation",
        toStation: "$toStation"
      },
      count: { $sum: 1 }
    }
  },
  {
    $lookup: {
      from: "station",
      let: {
        fromStation: "$_id.fromStation",
        toStation: "$_id.toStation"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $in: [
                "$_id",
                [
                  "$$fromStation",
                  "$$toStation"
                ]
              ]
            }
          }
        }
      ],
      as: "tmp"
    }
  },
  {
    $project: {
      _id: 0,
      fromStation: {
        $reduce: {
          input: "$tmp",
          initialValue: "",
          in: {
            $cond: [
              {
                $eq: [
                  "$_id.fromStation",
                  "$$this._id"
                ]
              },
              "$$this.name",
              "$$value"
            ]
          }
        }
      },
      toStation: {
        $reduce: {
          input: "$tmp",
          initialValue: "",
          in: {
            $cond: [
              {
                $eq: [
                  "$_id.toStation",
                  "$$this._id"
                ]
              },
              "$$this.name",
              "$$value"
            ]
          }
        }
      },
      count: 1
    }
  },
  {
    $sort: {
      fromStation: 1,
      toStation: 1
    }
  }
])
</code>```

Body:
I have ten stations stored in the stations collection: Station A, Station B, Station C, Station D, Station E, Station F, Station G, Station H, Station I, Station J.

Right now, to create a count list of all inter-station rides between all possible pairs of stations, I do the following in my Node.js code (using Mongoose):

const stationCombinations = []

// get all stations from the stations collection
const stationIds = await Station.find({}, '_id name').lean().exec()

// list of all possible from & to combinations with their names
stationIds.forEach(fromStation => {
  stationIds.forEach(toStation => {
    stationCombinations.push({ fromStation, toStation })
  })
})

const results = []

// loop through all station combinations
for (const stationCombination of stationCombinations) {
  // create aggregation query promise
  const data = Ride.aggregate([
    {
      $match: {
        test: false,
        state: 'completed',
        duration: { $gt: 2 },
        fromStation: mongoose.Types.ObjectId(stationCombination.fromStation._id),
        toStation: mongoose.Types.ObjectId(stationCombination.toStation._id)
       }
    },
    {
      $group: {
        _id: null,
        count: { $sum: 1 }
      }
    },
    {
      $addFields: {
        fromStation: stationCombination.fromStation.name,
        toStation: stationCombination.toStation.name
      }
    }
  ])

  // push promise to array
  results.push(data)
}

// run all aggregation queries
const stationData = await Promise.all(results)

// flatten nested/empty arrays and return
return stationData.flat()

Executing this function give me the result in this format:

[
  {
    "fromStation": "Station A",
    "toStation": "Station A",
    "count": 1196
  },
  {
    "fromStation": "Station A",
    "toStation": "Station B",
    "count": 1
  },
  {
    "fromStation": "Station A",
    "toStation": "Station C",
    "count": 173
  },
]

And so on for all other combinations...

The query currently takes a lot of time to execute and I keep getting alerts from MongoDB Atlas about excessive load on the database server because of these queries. Surely there must be an optimized way to do something like this?

Body:
You need to use MongoDB native operations. You need to $group by fromStation and toStation and with $lookup join two collections.

Note: I assume you have MongoDB >=v3.6 and Station._id is ObjectId

db.ride.aggregate([
  {
    $match: {
      test: false,
      state: "completed",
      duration: {
        $gt: 2
      }
    }
  },
  {
    $group: {
      _id: {
        fromStation: "$fromStation",
        toStation: "$toStation"
      },
      count: {
        $sum: 1
      }
    }
  },
  {
    $lookup: {
      from: "station",
      let: {
        fromStation: "$_id.fromStation",
        toStation: "$_id.toStation"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $in: [
                "$_id",
                [
                  "$$fromStation",
                  "$$toStation"
                ]
              ]
            }
          }
        }
      ],
      as: "tmp"
    }
  },
  {
    $project: {
      _id: 0,
      fromStation: {
        $reduce: {
          input: "$tmp",
          initialValue: "",
          in: {
            $cond: [
              {
                $eq: [
                  "$_id.fromStation",
                  "$$this._id"
                ]
              },
              "$$this.name",
              "$$value"
            ]
          }
        }
      },
      toStation: {
        $reduce: {
          input: "$tmp",
          initialValue: "",
          in: {
            $cond: [
              {
                $eq: [
                  "$_id.toStation",
                  "$$this._id"
                ]
              },
              "$$this.name",
              "$$value"
            ]
          }
        }
      },
      count: 1
    }
  },
  {
    $sort: {
      fromStation: 1,
      toStation: 1
    }
  }
])

MongoPlayground

Not tested:

const data = Ride.aggregate([
  {
     $match: {
       test: false,
       state: 'completed',
       duration: { $gt: 2 }
     }
  },
  {
    $group: {
      _id: {
        fromStation: "$fromStation",
        toStation: "$toStation"
      },
      count: { $sum: 1 }
    }
  },
  {
    $lookup: {
      from: "station",
      let: {
        fromStation: "$_id.fromStation",
        toStation: "$_id.toStation"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $in: [
                "$_id",
                [
                  "$$fromStation",
                  "$$toStation"
                ]
              ]
            }
          }
        }
      ],
      as: "tmp"
    }
  },
  {
    $project: {
      _id: 0,
      fromStation: {
        $reduce: {
          input: "$tmp",
          initialValue: "",
          in: {
            $cond: [
              {
                $eq: [
                  "$_id.fromStation",
                  "$$this._id"
                ]
              },
              "$$this.name",
              "$$value"
            ]
          }
        }
      },
      toStation: {
        $reduce: {
          input: "$tmp",
          initialValue: "",
          in: {
            $cond: [
              {
                $eq: [
                  "$_id.toStation",
                  "$$this._id"
                ]
              },
              "$$this.name",
              "$$value"
            ]
          }
        }
      },
      count: 1
    }
  },
  {
    $sort: {
      fromStation: 1,
      toStation: 1
    }
  }
])


问:
Regex: Duration (hours, minutes) in dart
I'm trying to sort a list of data using RegEx. Currently, the code below only checks for hours and minutes.

Example of the output currently:

1:22:00.000000

But I have some data that will also have just minutes  or just hours.

I'm looking to edit this code so it will check for all those possibilities:

Hours

Minutes

Hours and Minutes

```<code>final _regExp = RegExp(r'(?<hours>\d+)h (?<minutes>\d+)m');
Duration _parseDuration(String line) {
  final match = _regExp.firstMatch(line);

  if (match == null) {
    throw Exception('Could not get duration from: $line');
  }

  return Duration(
      hours: int.parse(match.namedGroup('hours')),
      minutes: int.parse(match.namedGroup('minutes')));
}
</code>```

I'm not sure how to do this?

Any help would be appreciated.

答:
Not sure if you can use the same name for the capturing group, but you could use an alternation to match optional hours and minutes or hours.

```<code>(?:(?:(\d+)h )?(\d+)m|(\d+)h)
</code>```

(?: Non capture group

(?:(\d+)h )? Capture group 1, match optional digits and h

(\d+)m Capture group 2, Match 1+ digits and m

| Or

(\d+)h Capture group 3, match 1+ digits and h

) Close group

Regex demo | Dart demo

The hours are in group 1 or 3, the minutes in group 2.

I am not familiar with Dart so there probably is a better way to write it, but for the idea I have added an example.

```<code>final _regExp = RegExp(r'(?:(?:(\d+)h )?(\d+)m|(\d+)h)');
Duration _parseDuration(String line) {
  final match = _regExp.firstMatch(line);

  if (match == null) {
    throw Exception('Could not get duration from: $line');
  }
  String h = match.group(1) ?? match.group(3) ?? "00";
  String m = match.group(2) ?? "00";

  return Duration(
      hours: int.parse(h),
      minutes: int.parse(m));
}
</code>```

Body:
I'm trying to sort a list of data using RegEx. Currently, the code below only checks for hours and minutes.

Example of the output currently:

1:22:00.000000

But I have some data that will also have just minutes  or just hours.

I'm looking to edit this code so it will check for all those possibilities:

Hours

Minutes

Hours and Minutes

final _regExp = RegExp(r'(?<hours>\d+)h (?<minutes>\d+)m');
Duration _parseDuration(String line) {
  final match = _regExp.firstMatch(line);

  if (match == null) {
    throw Exception('Could not get duration from: $line');
  }

  return Duration(
      hours: int.parse(match.namedGroup('hours')),
      minutes: int.parse(match.namedGroup('minutes')));
}

I'm not sure how to do this?

Any help would be appreciated.

Body:
Not sure if you can use the same name for the capturing group, but you could use an alternation to match optional hours and minutes or hours.

(?:(?:(\d+)h )?(\d+)m|(\d+)h)

(?: Non capture group

(?:(\d+)h )? Capture group 1, match optional digits and h

(\d+)m Capture group 2, Match 1+ digits and m

| Or

(\d+)h Capture group 3, match 1+ digits and h

) Close group

Regex demo | Dart demo

The hours are in group 1 or 3, the minutes in group 2.

I am not familiar with Dart so there probably is a better way to write it, but for the idea I have added an example.

final _regExp = RegExp(r'(?:(?:(\d+)h )?(\d+)m|(\d+)h)');
Duration _parseDuration(String line) {
  final match = _regExp.firstMatch(line);

  if (match == null) {
    throw Exception('Could not get duration from: $line');
  }
  String h = match.group(1) ?? match.group(3) ?? "00";
  String m = match.group(2) ?? "00";

  return Duration(
      hours: int.parse(h),
      minutes: int.parse(m));
}


问:
regexp and rails validations
I have two customs validations :

```<code>  def validate_email
    regexp = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+"
    if sleep_email.present? && !sleep_email.match(regexp)
      errors.add(:sleep_email, "l'email indiqué semble ne pas avoir le bon format")
    end
  end

  def validate_website
    regexp = "(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?"
    if website.present? && !website.match(regexp)
      errors.add(:website, "l'url de votre site web doit avoir la forme de http://votresite.com")
    end
  end
</code>```

But yo@yo and http://website are valids. What's wrong ?

Title: regexp and rails validations

Body:
I have two customs validations :

  def validate_email
    regexp = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+"
    if sleep_email.present? && !sleep_email.match(regexp)
      errors.add(:sleep_email, "l'email indiqué semble ne pas avoir le bon format")
    end
  end

  def validate_website
    regexp = "(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?"
    if website.present? && !website.match(regexp)
      errors.add(:website, "l'url de votre site web doit avoir la forme de http://votresite.com")
    end
  end

But yo@yo and http://website are valids. What's wrong ?

Body:
The standard email regex (RFC 5322 Official Standard) to use is:

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

As for the website URL, use this one. The URL will only be valid if the TLD (.com, .net, etc.) is included.

^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$


问:
How can I cast an @Binding in Swift
In swift I can cast an Int to a Double with Int(doubleVariable).

But how can I cast a Binding < Double >  to a Binding < Int >  ?

Then I could pass an @Binding var intVar: Int to a function requiring a Double binding.

(The context of this is that I have an Int which I want to pass to a Slider in SwiftUI.)

答:
I don’t think there’s a way to cast but you can create another Binding based on the first one:</p>

```<code>import SwiftUI
var d = 1.0
// Create a Binding<Double> (for lack of another source)
var bd = Binding<Double>(get: { d }, set: { d = $0 })
// "Convert" to Binding<Int> by recreating another Binding
let bi = Binding<Int>(get: { Int(bd.wrappedValue) },
                      set: { bd.wrappedValue = Double($0) })
</code>```

Title: How can I cast an @Binding in Swift

Body:
I don’t think there’s a way to cast but you can create another Binding based on the first one:

import SwiftUI
var d = 1.0
// Create a Binding<Double> (for lack of another source)
var bd = Binding<Double>(get: { d }, set: { d = $0 })
// "Convert" to Binding<Int> by recreating another Binding
let bi = Binding<Int>(get: { Int(bd.wrappedValue) },
                      set: { bd.wrappedValue = Double($0) })