问:
Flutter - Drag Animation is not returning to the start after ListView update
The animation of ListView items that is about dragging to the left and the delete button appears works fine. The problem is that if I leave the delete button appearing and changing the page, when I return pop() to the previous page the button keeps appearing.

The animation did not go back to the beginning. The same happens if I update the items in the ListView, for example deleting an item. The item is deleted but the animation is not.

It looks like I'm updating the ListView content and not the ListView itself.

How could you solve this problem?

I'm trying to destroy ListView with every update in its items, but I'm not getting it. I also do not know if this is the right way to solve the problem.

The ListView is built through the buildTile() function and its items come from the database.

Below I put the code and a gif demonstrating what the problem is.

To work the code below, you need to insert the sqflite and path_provider dependencies into pubspec.yaml, thus:

```<code>dependencies:
  sqflite: any
  path_provider: any
  flutter:
    sdk: flutter
</code>```

```<code>import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:ui' as ui;

enum DialogOptionsAction {
  cancel,
  ok
}

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
      routes: <String, WidgetBuilder> {
        '/newpage': (BuildContext context) => new NewPage(),
      },
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  DatabaseClient _db = new DatabaseClient();
  List listCategory = [];
  List<Widget> tiles;

  List colors = [
    const Color(0xFFFFA500),
    const Color(0xFF279605),
    const Color(0xFF005959)
  ];

  createdb() async {
    await _db.create().then(
      (data){
        _db.getAllCategory().then((list){
          setState(() {
            this.listCategory = list;
          });
        });
      }
    );
  }

  @override
  void initState() {
    super.initState();
    createdb();
  }

  void showCategoryDelete<T>({ BuildContext context, Widget child }) {
    showDialog<T>(
      context: context,
      child: child,
    )
    .then<Null>((T value) {
      if (value != null) {
        setState(() { print(value); });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> buildTile(List list) {
      this.tiles = [];
      for(var dict in list) {
        this.tiles.add(
          new ItemCategory(
            id: dict['id'],
            category: dict['name'],
            color: this.colors[dict['color']],
            onPressed: () async {
              showCategoryDelete<DialogOptionsAction>(
                context: context,
                child: new AlertDialog(
                  title: const Text('Delete Category'),
                  content: new Text(
                    'Do you want to delete this category?',
                      style: new TextStyle(
                        color: Colors.black26,
                        fontSize: 16.0,
                        fontFamily: "Roboto",
                        fontWeight: FontWeight.w500,
                      )
                  ),
                  actions: <Widget>[
                    new FlatButton(
                      child: const Text('CANCEL'),
                      onPressed: () {
                        Navigator.pop(context);
                      }
                    ),
                    new FlatButton(
                      child: const Text('OK'),
                      onPressed: () {
                        _db.deleteCategory(dict['id']).then(
                          (list) {
                            setState(() {
                              this.listCategory = list;
                            });
                          }
                        );
                        Navigator.pop(context);
                      }
                    )
                  ]
                )
              );
            },
          )
        );
      }
      return this.tiles;
    }
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Categories'),
        actions: <Widget>[
          new IconButton(
            icon: const Icon(Icons.add),
            color: new Color(0xFFFFFFFF),
            onPressed: () async {
              await Navigator.of(context).pushNamed('/newpage').then(
                (data){
                  _db.getAllCategory().then((list){
                    setState(() {
                      this.listCategory = list;
                    });
                  });
                }
              );
            }
          )
        ],
      ),
      body: new ListView(
        padding: new EdgeInsets.only(top: 8.0, right: 0.0, left: 0.0),
        children: buildTile(this.listCategory)
      )
    );
  }
}

class NewPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('New Page'),
      ),
    );
  }
}

//Creating Database with some data and two queries
class DatabaseClient {
  Database db;

  Future create() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    db = await openDatabase(dbPath, version: 1, onCreate: this._create);
  }

  Future _create(Database db, int version) async {
    await db.execute("""
      CREATE TABLE category (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        color INTEGER NOT NULL
      )""");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)");
  }

  Future getAllCategory() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    Database db = await openDatabase(dbPath);

    List list = await db.rawQuery('SELECT * FROM category');
    await db.close();

    return list;
  }

  Future deleteCategory(int id) async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    Database db = await openDatabase(dbPath);

    await db.delete('category', where: "id = ?", whereArgs: [id]);
    List list = await db.rawQuery('SELECT * FROM category');

    await db.close();

    return list;
  }
}

//Creating ListViews items
class ItemCategory extends StatefulWidget {
  ItemCategory({ Key key, this.id, this.category, this.color, this.onPressed}) : super(key: key);

  final int id;
  final String category;
  final Color color;
  final VoidCallback onPressed;

  @override
  ItemCategoryState createState() => new ItemCategoryState();
}

class ItemCategoryState extends State<ItemCategory> with TickerProviderStateMixin {
  ItemCategoryState();

  DatabaseClient db = new DatabaseClient();
  AnimationController _controller;
  Animation<double> _animation;
  double flingOpening;
  bool startFling = true;

  void initState() {
    super.initState();
    _controller = new AnimationController(duration:
      const Duration(milliseconds: 246), vsync: this);

    _animation = new CurvedAnimation(
      parent: _controller,
      curve: new Interval(0.0, 1.0, curve: Curves.linear),
    );
  }

  void _move(DragUpdateDetails details) {
    final double delta = details.primaryDelta / 304;
    _controller.value -= delta;
  }

  void _settle(DragEndDetails details) {
    if(this.startFling) {
      _controller.fling(velocity: 1.0);
      this.startFling = false;
    } else if(!this.startFling){
      _controller.fling(velocity: -1.0);
      this.startFling = true;
    }
  }

  @override
  Widget build(BuildContext context) {
    final ui.Size logicalSize = MediaQuery.of(context).size;
    final double _width = logicalSize.width;
    this.flingOpening = -(48.0/_width);

    return new GestureDetector(
      onHorizontalDragUpdate: _move,
      onHorizontalDragEnd: _settle,
      child: new Stack(
        children: <Widget>[
          new Positioned.fill(
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                new Container(
                  decoration: new BoxDecoration(
                    color: new Color(0xFFE57373),
                  ),
                  child: new IconButton(
                    icon: new Icon(Icons.delete),
                    color: new Color(0xFFFFFFFF),
                    onPressed: widget.onPressed
                  )
                ),
              ],
            ),
          ),
          new SlideTransition(
            position: new Tween<Offset>(
              begin:  Offset.zero,
              end: new Offset(this.flingOpening, 0.0),
            ).animate(_animation),
            child: new Container(
              decoration: new BoxDecoration(
                border: new Border(
                  top: new BorderSide(style: BorderStyle.solid, color: Colors.black26),
                ),
                color: new Color(0xFFFFFFFF),
              ),
              margin: new EdgeInsets.only(top: 0.0, bottom: 0.0),
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  new Expanded(
                    child: new Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: <Widget>[
                        new Container(
                          margin: new EdgeInsets.only(left: 16.0),
                          padding: new EdgeInsets.only(right: 40.0, top: 4.5, bottom: 4.5),
                          child: new Row(
                            children: <Widget>[
                              new Container(
                                margin: new EdgeInsets.only(right: 16.0),
                                child: new Icon(
                                  Icons.brightness_1,
                                  color: widget.color,
                                  size: 35.0,
                                ),
                              ),
                              new Text(
                                widget.category,
                                style: new TextStyle(
                                  color: Colors.black87,
                                  fontSize: 14.0,
                                  fontFamily: "Roboto",
                                  fontWeight: FontWeight.w500,
                                ),
                              ),
                            ],
                          )
                        )
                      ],
                    ),
                  )
                ],
              ),
            )
          ),
        ],
      )
    );
  }
}
</code>```

答:
According to the Flutter - Widget animation status remains even after it has been removed

Simply insert the key into the new ItemCategory (

I needed to pass a key to the children. Or else the renderer won't be able to know which SlideTransition got removed, and use the index.

```<code>...
@override
Widget build(BuildContext context) {
  List<Widget> buildTile(List list) {
    this.tiles = [];
    for(var dict in list) {
      this.tiles.add(
        new ItemCategory(
          key: new Key(dict), //new
          id: dict['id'],
          category: dict['name'],
...
</code>```

The complete code is:

```<code>import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:ui' as ui;

enum DialogOptionsAction {
  cancel,
  ok
}

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
      routes: <String, WidgetBuilder> {
        '/newpage': (BuildContext context) => new NewPage(),
      },
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  DatabaseClient _db = new DatabaseClient();
  List listCategory = [];
  List<Widget> tiles;

  List colors = [
    const Color(0xFFFFA500),
    const Color(0xFF279605),
    const Color(0xFF005959)
  ];

  createdb() async {
    await _db.create().then(
      (data){
        _db.getAllCategory().then((list){
          setState(() {
            this.listCategory = list;
          });
        });
      }
    );
  }

  @override
  void initState() {
    super.initState();
    createdb();
  }

  void showCategoryDelete<T>({ BuildContext context, Widget child }) {
    showDialog<T>(
      context: context,
      child: child,
    )
    .then<Null>((T value) {
      if (value != null) {
        setState(() { print(value); });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> buildTile(List list) {
      this.tiles = [];
      for(var dict in list) {
        this.tiles.add(
          new ItemCategory(
            key: new Key(dict), //new
            id: dict['id'],
            category: dict['name'],
            color: this.colors[dict['color']],
            onPressed: () async {
              showCategoryDelete<DialogOptionsAction>(
                context: context,
                child: new AlertDialog(
                  title: const Text('Delete Category'),
                  content: new Text(
                    'Do you want to delete this category?',
                      style: new TextStyle(
                        color: Colors.black26,
                        fontSize: 16.0,
                        fontFamily: "Roboto",
                        fontWeight: FontWeight.w500,
                      )
                  ),
                  actions: <Widget>[
                    new FlatButton(
                      child: const Text('CANCEL'),
                      onPressed: () {
                        Navigator.pop(context);
                      }
                    ),
                    new FlatButton(
                      child: const Text('OK'),
                      onPressed: () {
                        _db.deleteCategory(dict['id']).then(
                          (list) {
                            setState(() {
                              this.listCategory = list;
                            });
                          }
                        );
                        Navigator.pop(context);
                      }
                    )
                  ]
                )
              );
            },
          )
        );
      }
      return this.tiles;
    }
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Categories'),
        actions: <Widget>[
          new IconButton(
            icon: const Icon(Icons.add),
            color: new Color(0xFFFFFFFF),
            onPressed: () async {
              await Navigator.of(context).pushNamed('/newpage').then(
                (data){
                  _db.getAllCategory().then((list){
                    setState(() {
                      this.listCategory = list;
                    });
                  });
                }
              );
            }
          )
        ],
      ),
      body: new ListView(
        padding: new EdgeInsets.only(top: 8.0, right: 0.0, left: 0.0),
        children: buildTile(this.listCategory)
      )
    );
  }
}

class NewPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('New Page'),
      ),
    );
  }
}

//Creating Database with some data and two queries
class DatabaseClient {
  Database db;

  Future create() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    db = await openDatabase(dbPath, version: 1, onCreate: this._create);
  }

  Future _create(Database db, int version) async {
    await db.execute("""
      CREATE TABLE category (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        color INTEGER NOT NULL
      )""");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)");
  }

  Future getAllCategory() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    Database db = await openDatabase(dbPath);

    List list = await db.rawQuery('SELECT * FROM category');
    await db.close();

    return list;
  }

  Future deleteCategory(int id) async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    Database db = await openDatabase(dbPath);

    await db.delete('category', where: "id = ?", whereArgs: [id]);
    List list = await db.rawQuery('SELECT * FROM category');

    await db.close();

    return list;
  }
}

//Creating ListViews items
class ItemCategory extends StatefulWidget {
  ItemCategory({ Key key, this.id, this.category, this.color, this.onPressed}) : super(key: key);

  final int id;
  final String category;
  final Color color;
  final VoidCallback onPressed;

  @override
  ItemCategoryState createState() => new ItemCategoryState();
}

class ItemCategoryState extends State<ItemCategory> with TickerProviderStateMixin {
  ItemCategoryState();

  DatabaseClient db = new DatabaseClient();
  AnimationController _controller;
  Animation<double> _animation;
  double flingOpening;
  bool startFling = true;

  void initState() {
    super.initState();
    _controller = new AnimationController(duration:
      const Duration(milliseconds: 246), vsync: this);

    _animation = new CurvedAnimation(
      parent: _controller,
      curve: new Interval(0.0, 1.0, curve: Curves.linear),
    );
  }

  void _move(DragUpdateDetails details) {
    final double delta = details.primaryDelta / 304;
    _controller.value -= delta;
  }

  void _settle(DragEndDetails details) {
    if(this.startFling) {
      _controller.fling(velocity: 1.0);
      this.startFling = false;
    } else if(!this.startFling){
      _controller.fling(velocity: -1.0);
      this.startFling = true;
    }
  }

  @override
  Widget build(BuildContext context) {
    final ui.Size logicalSize = MediaQuery.of(context).size;
    final double _width = logicalSize.width;
    this.flingOpening = -(48.0/_width);

    return new GestureDetector(
      onHorizontalDragUpdate: _move,
      onHorizontalDragEnd: _settle,
      child: new Stack(
        children: <Widget>[
          new Positioned.fill(
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                new Container(
                  decoration: new BoxDecoration(
                    color: new Color(0xFFE57373),
                  ),
                  child: new IconButton(
                    icon: new Icon(Icons.delete),
                    color: new Color(0xFFFFFFFF),
                    onPressed: widget.onPressed
                  )
                ),
              ],
            ),
          ),
          new SlideTransition(
            position: new Tween<Offset>(
              begin:  Offset.zero,
              end: new Offset(this.flingOpening, 0.0),
            ).animate(_animation),
            child: new Container(
              decoration: new BoxDecoration(
                border: new Border(
                  top: new BorderSide(style: BorderStyle.solid, color: Colors.black26),
                ),
                color: new Color(0xFFFFFFFF),
              ),
              margin: new EdgeInsets.only(top: 0.0, bottom: 0.0),
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  new Expanded(
                    child: new Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: <Widget>[
                        new Container(
                          margin: new EdgeInsets.only(left: 16.0),
                          padding: new EdgeInsets.only(right: 40.0, top: 4.5, bottom: 4.5),
                          child: new Row(
                            children: <Widget>[
                              new Container(
                                margin: new EdgeInsets.only(right: 16.0),
                                child: new Icon(
                                  Icons.brightness_1,
                                  color: widget.color,
                                  size: 35.0,
                                ),
                              ),
                              new Text(
                                widget.category,
                                style: new TextStyle(
                                  color: Colors.black87,
                                  fontSize: 14.0,
                                  fontFamily: "Roboto",
                                  fontWeight: FontWeight.w500,
                                ),
                              ),
                            ],
                          )
                        )
                      ],
                    ),
                  )
                ],
              ),
            )
          ),
        ],
      )
    );
  }
}
</code>```

Body:
The animation of ListView items that is about dragging to the left and the delete button appears works fine. The problem is that if I leave the delete button appearing and changing the page, when I return pop() to the previous page the button keeps appearing.

The animation did not go back to the beginning. The same happens if I update the items in the ListView, for example deleting an item. The item is deleted but the animation is not.

It looks like I'm updating the ListView content and not the ListView itself.

How could you solve this problem?

I'm trying to destroy ListView with every update in its items, but I'm not getting it. I also do not know if this is the right way to solve the problem.

The ListView is built through the buildTile() function and its items come from the database.

Below I put the code and a gif demonstrating what the problem is.

To work the code below, you need to insert the sqflite and path_provider dependencies into pubspec.yaml, thus:

dependencies:
  sqflite: any
  path_provider: any
  flutter:
    sdk: flutter

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:ui' as ui;

enum DialogOptionsAction {
  cancel,
  ok
}

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
      routes: <String, WidgetBuilder> {
        '/newpage': (BuildContext context) => new NewPage(),
      },
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  DatabaseClient _db = new DatabaseClient();
  List listCategory = [];
  List<Widget> tiles;

  List colors = [
    const Color(0xFFFFA500),
    const Color(0xFF279605),
    const Color(0xFF005959)
  ];

  createdb() async {
    await _db.create().then(
      (data){
        _db.getAllCategory().then((list){
          setState(() {
            this.listCategory = list;
          });
        });
      }
    );
  }

  @override
  void initState() {
    super.initState();
    createdb();
  }

  void showCategoryDelete<T>({ BuildContext context, Widget child }) {
    showDialog<T>(
      context: context,
      child: child,
    )
    .then<Null>((T value) {
      if (value != null) {
        setState(() { print(value); });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> buildTile(List list) {
      this.tiles = [];
      for(var dict in list) {
        this.tiles.add(
          new ItemCategory(
            id: dict['id'],
            category: dict['name'],
            color: this.colors[dict['color']],
            onPressed: () async {
              showCategoryDelete<DialogOptionsAction>(
                context: context,
                child: new AlertDialog(
                  title: const Text('Delete Category'),
                  content: new Text(
                    'Do you want to delete this category?',
                      style: new TextStyle(
                        color: Colors.black26,
                        fontSize: 16.0,
                        fontFamily: "Roboto",
                        fontWeight: FontWeight.w500,
                      )
                  ),
                  actions: <Widget>[
                    new FlatButton(
                      child: const Text('CANCEL'),
                      onPressed: () {
                        Navigator.pop(context);
                      }
                    ),
                    new FlatButton(
                      child: const Text('OK'),
                      onPressed: () {
                        _db.deleteCategory(dict['id']).then(
                          (list) {
                            setState(() {
                              this.listCategory = list;
                            });
                          }
                        );
                        Navigator.pop(context);
                      }
                    )
                  ]
                )
              );
            },
          )
        );
      }
      return this.tiles;
    }
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Categories'),
        actions: <Widget>[
          new IconButton(
            icon: const Icon(Icons.add),
            color: new Color(0xFFFFFFFF),
            onPressed: () async {
              await Navigator.of(context).pushNamed('/newpage').then(
                (data){
                  _db.getAllCategory().then((list){
                    setState(() {
                      this.listCategory = list;
                    });
                  });
                }
              );
            }
          )
        ],
      ),
      body: new ListView(
        padding: new EdgeInsets.only(top: 8.0, right: 0.0, left: 0.0),
        children: buildTile(this.listCategory)
      )
    );
  }
}

class NewPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('New Page'),
      ),
    );
  }
}

//Creating Database with some data and two queries
class DatabaseClient {
  Database db;

  Future create() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    db = await openDatabase(dbPath, version: 1, onCreate: this._create);
  }

  Future _create(Database db, int version) async {
    await db.execute("""
      CREATE TABLE category (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        color INTEGER NOT NULL
      )""");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)");
  }

  Future getAllCategory() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    Database db = await openDatabase(dbPath);

    List list = await db.rawQuery('SELECT * FROM category');
    await db.close();

    return list;
  }

  Future deleteCategory(int id) async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    Database db = await openDatabase(dbPath);

    await db.delete('category', where: "id = ?", whereArgs: [id]);
    List list = await db.rawQuery('SELECT * FROM category');

    await db.close();

    return list;
  }
}

//Creating ListViews items
class ItemCategory extends StatefulWidget {
  ItemCategory({ Key key, this.id, this.category, this.color, this.onPressed}) : super(key: key);

  final int id;
  final String category;
  final Color color;
  final VoidCallback onPressed;

  @override
  ItemCategoryState createState() => new ItemCategoryState();
}

class ItemCategoryState extends State<ItemCategory> with TickerProviderStateMixin {
  ItemCategoryState();

  DatabaseClient db = new DatabaseClient();
  AnimationController _controller;
  Animation<double> _animation;
  double flingOpening;
  bool startFling = true;

  void initState() {
    super.initState();
    _controller = new AnimationController(duration:
      const Duration(milliseconds: 246), vsync: this);

    _animation = new CurvedAnimation(
      parent: _controller,
      curve: new Interval(0.0, 1.0, curve: Curves.linear),
    );
  }

  void _move(DragUpdateDetails details) {
    final double delta = details.primaryDelta / 304;
    _controller.value -= delta;
  }

  void _settle(DragEndDetails details) {
    if(this.startFling) {
      _controller.fling(velocity: 1.0);
      this.startFling = false;
    } else if(!this.startFling){
      _controller.fling(velocity: -1.0);
      this.startFling = true;
    }
  }

  @override
  Widget build(BuildContext context) {
    final ui.Size logicalSize = MediaQuery.of(context).size;
    final double _width = logicalSize.width;
    this.flingOpening = -(48.0/_width);

    return new GestureDetector(
      onHorizontalDragUpdate: _move,
      onHorizontalDragEnd: _settle,
      child: new Stack(
        children: <Widget>[
          new Positioned.fill(
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                new Container(
                  decoration: new BoxDecoration(
                    color: new Color(0xFFE57373),
                  ),
                  child: new IconButton(
                    icon: new Icon(Icons.delete),
                    color: new Color(0xFFFFFFFF),
                    onPressed: widget.onPressed
                  )
                ),
              ],
            ),
          ),
          new SlideTransition(
            position: new Tween<Offset>(
              begin:  Offset.zero,
              end: new Offset(this.flingOpening, 0.0),
            ).animate(_animation),
            child: new Container(
              decoration: new BoxDecoration(
                border: new Border(
                  top: new BorderSide(style: BorderStyle.solid, color: Colors.black26),
                ),
                color: new Color(0xFFFFFFFF),
              ),
              margin: new EdgeInsets.only(top: 0.0, bottom: 0.0),
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  new Expanded(
                    child: new Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: <Widget>[
                        new Container(
                          margin: new EdgeInsets.only(left: 16.0),
                          padding: new EdgeInsets.only(right: 40.0, top: 4.5, bottom: 4.5),
                          child: new Row(
                            children: <Widget>[
                              new Container(
                                margin: new EdgeInsets.only(right: 16.0),
                                child: new Icon(
                                  Icons.brightness_1,
                                  color: widget.color,
                                  size: 35.0,
                                ),
                              ),
                              new Text(
                                widget.category,
                                style: new TextStyle(
                                  color: Colors.black87,
                                  fontSize: 14.0,
                                  fontFamily: "Roboto",
                                  fontWeight: FontWeight.w500,
                                ),
                              ),
                            ],
                          )
                        )
                      ],
                    ),
                  )
                ],
              ),
            )
          ),
        ],
      )
    );
  }
}

Body:
According to the Flutter - Widget animation status remains even after it has been removed

Simply insert the key into the new ItemCategory (

I needed to pass a key to the children. Or else the renderer won't be able to know which SlideTransition got removed, and use the index.

...
@override
Widget build(BuildContext context) {
  List<Widget> buildTile(List list) {
    this.tiles = [];
    for(var dict in list) {
      this.tiles.add(
        new ItemCategory(
          key: new Key(dict), //new
          id: dict['id'],
          category: dict['name'],
...

The complete code is:

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:ui' as ui;

enum DialogOptionsAction {
  cancel,
  ok
}

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
      routes: <String, WidgetBuilder> {
        '/newpage': (BuildContext context) => new NewPage(),
      },
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  DatabaseClient _db = new DatabaseClient();
  List listCategory = [];
  List<Widget> tiles;

  List colors = [
    const Color(0xFFFFA500),
    const Color(0xFF279605),
    const Color(0xFF005959)
  ];

  createdb() async {
    await _db.create().then(
      (data){
        _db.getAllCategory().then((list){
          setState(() {
            this.listCategory = list;
          });
        });
      }
    );
  }

  @override
  void initState() {
    super.initState();
    createdb();
  }

  void showCategoryDelete<T>({ BuildContext context, Widget child }) {
    showDialog<T>(
      context: context,
      child: child,
    )
    .then<Null>((T value) {
      if (value != null) {
        setState(() { print(value); });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> buildTile(List list) {
      this.tiles = [];
      for(var dict in list) {
        this.tiles.add(
          new ItemCategory(
            key: new Key(dict), //new
            id: dict['id'],
            category: dict['name'],
            color: this.colors[dict['color']],
            onPressed: () async {
              showCategoryDelete<DialogOptionsAction>(
                context: context,
                child: new AlertDialog(
                  title: const Text('Delete Category'),
                  content: new Text(
                    'Do you want to delete this category?',
                      style: new TextStyle(
                        color: Colors.black26,
                        fontSize: 16.0,
                        fontFamily: "Roboto",
                        fontWeight: FontWeight.w500,
                      )
                  ),
                  actions: <Widget>[
                    new FlatButton(
                      child: const Text('CANCEL'),
                      onPressed: () {
                        Navigator.pop(context);
                      }
                    ),
                    new FlatButton(
                      child: const Text('OK'),
                      onPressed: () {
                        _db.deleteCategory(dict['id']).then(
                          (list) {
                            setState(() {
                              this.listCategory = list;
                            });
                          }
                        );
                        Navigator.pop(context);
                      }
                    )
                  ]
                )
              );
            },
          )
        );
      }
      return this.tiles;
    }
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Categories'),
        actions: <Widget>[
          new IconButton(
            icon: const Icon(Icons.add),
            color: new Color(0xFFFFFFFF),
            onPressed: () async {
              await Navigator.of(context).pushNamed('/newpage').then(
                (data){
                  _db.getAllCategory().then((list){
                    setState(() {
                      this.listCategory = list;
                    });
                  });
                }
              );
            }
          )
        ],
      ),
      body: new ListView(
        padding: new EdgeInsets.only(top: 8.0, right: 0.0, left: 0.0),
        children: buildTile(this.listCategory)
      )
    );
  }
}

class NewPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('New Page'),
      ),
    );
  }
}

//Creating Database with some data and two queries
class DatabaseClient {
  Database db;

  Future create() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    db = await openDatabase(dbPath, version: 1, onCreate: this._create);
  }

  Future _create(Database db, int version) async {
    await db.execute("""
      CREATE TABLE category (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        color INTEGER NOT NULL
      )""");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)");
  }

  Future getAllCategory() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    Database db = await openDatabase(dbPath);

    List list = await db.rawQuery('SELECT * FROM category');
    await db.close();

    return list;
  }

  Future deleteCategory(int id) async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    Database db = await openDatabase(dbPath);

    await db.delete('category', where: "id = ?", whereArgs: [id]);
    List list = await db.rawQuery('SELECT * FROM category');

    await db.close();

    return list;
  }
}

//Creating ListViews items
class ItemCategory extends StatefulWidget {
  ItemCategory({ Key key, this.id, this.category, this.color, this.onPressed}) : super(key: key);

  final int id;
  final String category;
  final Color color;
  final VoidCallback onPressed;

  @override
  ItemCategoryState createState() => new ItemCategoryState();
}

class ItemCategoryState extends State<ItemCategory> with TickerProviderStateMixin {
  ItemCategoryState();

  DatabaseClient db = new DatabaseClient();
  AnimationController _controller;
  Animation<double> _animation;
  double flingOpening;
  bool startFling = true;

  void initState() {
    super.initState();
    _controller = new AnimationController(duration:
      const Duration(milliseconds: 246), vsync: this);

    _animation = new CurvedAnimation(
      parent: _controller,
      curve: new Interval(0.0, 1.0, curve: Curves.linear),
    );
  }

  void _move(DragUpdateDetails details) {
    final double delta = details.primaryDelta / 304;
    _controller.value -= delta;
  }

  void _settle(DragEndDetails details) {
    if(this.startFling) {
      _controller.fling(velocity: 1.0);
      this.startFling = false;
    } else if(!this.startFling){
      _controller.fling(velocity: -1.0);
      this.startFling = true;
    }
  }

  @override
  Widget build(BuildContext context) {
    final ui.Size logicalSize = MediaQuery.of(context).size;
    final double _width = logicalSize.width;
    this.flingOpening = -(48.0/_width);

    return new GestureDetector(
      onHorizontalDragUpdate: _move,
      onHorizontalDragEnd: _settle,
      child: new Stack(
        children: <Widget>[
          new Positioned.fill(
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                new Container(
                  decoration: new BoxDecoration(
                    color: new Color(0xFFE57373),
                  ),
                  child: new IconButton(
                    icon: new Icon(Icons.delete),
                    color: new Color(0xFFFFFFFF),
                    onPressed: widget.onPressed
                  )
                ),
              ],
            ),
          ),
          new SlideTransition(
            position: new Tween<Offset>(
              begin:  Offset.zero,
              end: new Offset(this.flingOpening, 0.0),
            ).animate(_animation),
            child: new Container(
              decoration: new BoxDecoration(
                border: new Border(
                  top: new BorderSide(style: BorderStyle.solid, color: Colors.black26),
                ),
                color: new Color(0xFFFFFFFF),
              ),
              margin: new EdgeInsets.only(top: 0.0, bottom: 0.0),
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  new Expanded(
                    child: new Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: <Widget>[
                        new Container(
                          margin: new EdgeInsets.only(left: 16.0),
                          padding: new EdgeInsets.only(right: 40.0, top: 4.5, bottom: 4.5),
                          child: new Row(
                            children: <Widget>[
                              new Container(
                                margin: new EdgeInsets.only(right: 16.0),
                                child: new Icon(
                                  Icons.brightness_1,
                                  color: widget.color,
                                  size: 35.0,
                                ),
                              ),
                              new Text(
                                widget.category,
                                style: new TextStyle(
                                  color: Colors.black87,
                                  fontSize: 14.0,
                                  fontFamily: "Roboto",
                                  fontWeight: FontWeight.w500,
                                ),
                              ),
                            ],
                          )
                        )
                      ],
                    ),
                  )
                ],
              ),
            )
          ),
        ],
      )
    );
  }
}


来源: stackexchange

问:
Php File content into Multiple Talbes
I have a File with over 360.000 Rows, every line is a JSON.

i read the Json into Php Variables and insert it into two Tables

"movies" and "movies_details"

i'm using Laravel 5.4.
My Problem is the Performance.
so i can insert round about 35-40 rows in one minute with this code

```<code>$idMovie = DB::table('movies')
   ->insertGetId([
       'moviedb_id' => $id,
       'adult' => $adult,
       'created_at' => $dateTime,
  ]);
  DB::table('movies_title')
      ->insert([
          'movie_id' => $idMovie,
          'original_title' => $original_title,
          'created_at' => $dateTime,
      ]);
</code>```

but i thing anyone know a faster and better way to insert this.

Thanks for your time.

答:
you could use multiple insert (bulk insert) assuming you can assign your vars an array

as in flat sql

```<code>    insert into your_table (moviedb_id, adult, created_at)
    values ($id[0],  $adult[0], $dateTime[0]),
            ($id[1],  $adult[1], $dateTime[1]),

            ......
            ......
            ($id[n],  $adult[n], $dateTime[n])
</code>```

in this way with  each insert you insert more rows  and instead  of 30 - 40 for minute you can insert 30*n - 40*n rows

so check for yout ORM or framework query manager for the equivalent function

ContentLicense: CC BY-SA 3.0

Title: Php File content into Multiple Talbes

Body:
I have a File with over 360.000 Rows, every line is a JSON.

i read the Json into Php Variables and insert it into two Tables

"movies" and "movies_details"

i'm using Laravel 5.4.
My Problem is the Performance.
so i can insert round about 35-40 rows in one minute with this code

$idMovie = DB::table('movies')
   ->insertGetId([
       'moviedb_id' => $id,
       'adult' => $adult,
       'created_at' => $dateTime,
  ]);
  DB::table('movies_title')
      ->insert([
          'movie_id' => $idMovie,
          'original_title' => $original_title,
          'created_at' => $dateTime,
      ]);

but i thing anyone know a faster and better way to insert this.

Thanks for your time.

Body:
you could use multiple insert (bulk insert) assuming you can assign your vars an array

as in flat sql

    insert into your_table (moviedb_id, adult, created_at)
    values ($id[0],  $adult[0], $dateTime[0]),
            ($id[1],  $adult[1], $dateTime[1]),

            ......
            ......
            ($id[n],  $adult[n], $dateTime[n])

in this way with  each insert you insert more rows  and instead  of 30 - 40 for minute you can insert 30*n - 40*n rows

so check for yout ORM or framework query manager for the equivalent function


问:
Unwrapping two optionals from Text Field
I'm writing a simply app that includes calculating an area from user input of ID and OD in text fields. The label for the area is hidden until both ID and OD are entered. I'm having trouble unwrapping the optionals of ID and OD text field, to avoid error if user presses "return" without putting any number. This is what I wrote, which I thought would not run the area calculation codes unless both fields contain numbers (I've restricted keyboard to numbers only), but when I press "return" in either field without entering anything, I'm still getting the "nil optional error" in the area calculation code.

//move onto next field after return
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
            nextField.becomeFirstResponder()
        } else{
            textField.resignFirstResponder()
        }
        //calculate and display gasket area
        if textField == self.gasketODInput || textField == self.gasketIDInput {
            if let gasketIDString = gasketIDInput?.text, let gasketODString = gasketODInput?.text{
                    gasketArea = 0.7854*(Float(gasketODString)!*Float(gasketODString)!-Float(gasketIDString)!*Float(gasketIDString)!)
                    gasketAreaLabel.text = String(gasketArea)
                    self.gasketAreaLabel.isHidden = false
            }
        }
        return false
    }
```

答:
You need to safely unwrap the attempts to convert the text to Float.

```<code>if let gasketIDString = gasketIDInput?.text,
   let gasketODString = gasketODInput?.text,
   let gasketOD = Float(gasketODString),
   let gasketID = Float(gasketIDString) {

    gasketArea = 0.7854 * (gasketOD * gasketOD - gasketID * gasketID)
    gasketAreaLabel.text = String(gasketArea)
    self.gasketAreaLabel.isHidden = false
}
</code>```

Body:
You need to safely unwrap the attempts to convert the text to Float.

if let gasketIDString = gasketIDInput?.text,
   let gasketODString = gasketODInput?.text,
   let gasketOD = Float(gasketODString),
   let gasketID = Float(gasketIDString) {

    gasketArea = 0.7854 * (gasketOD * gasketOD - gasketID * gasketID)
    gasketAreaLabel.text = String(gasketArea)
    self.gasketAreaLabel.isHidden = false
}


问:
I need some help here with SQL Sum function group by dates and group
I have a table called Table1

```
CaseNumber | Year  | Month | Group    | HoursAttended
1          | 2010  |   1   |   16-20  |  8
2          | 2010  |   1   |   16-20  |  1
3          | 2010  |   2   |    0-5   |  2
4          | 2009  |   4   |    0-5   |  2
4          | 2009  |   4   |  16-20   |  5
```

If I wanted to have a result like this

```
Year | Month |GroupHours| GroupHourTotal
2010 | 1     |16-20     | 9
2010 | 3     |2         | 2
2009 | 4     |0-5       | 2
2009 | 4     |16-20     | 5
```

I tried this but I am getting incorrect results when the dates are not in the same month.
Any ideas?

```select Year,Month,group,hourattended,sum(hourattended)
from Table1
groupby Year,Month,hourattended,sum(hourattended)
order by year desc
```

答:
Only group by the columns that are not aggregates in the result set. Since you're summing HoursAttended, it does not belong in your GROUP BY.

select Year,Month,group,hourattended,sum(hourattended)
from Table1
groupby Year,Month
order by year desc

ContentLicense: CC BY-SA 2.5

Body:
I have a table called Table1

CaseNumber | Year  | Month | Group    | HoursAttended
1          | 2010  |   1   |   16-20  |  8
2          | 2010  |   1   |   16-20  |  1
3          | 2010  |   2   |    0-5   |  2
4          | 2009  |   4   |    0-5   |  2
4          | 2009  |   4   |  16-20   |  5

If I wanted to have a result like this

Year | Month |GroupHours| GroupHourTotal
2010 | 1     |16-20     | 9
2010 | 3     |2         | 2
2009 | 4     |0-5       | 2
2009 | 4     |16-20     | 5

I tried this but I am getting incorrect results when the dates are not in the same month.
Any ideas?

select Year,Month,group,hourattended,sum(hourattended)
from Table1
groupby Year,Month,hourattended,sum(hourattended)
order by year desc


问:
Make two characters move at the same time
I'm trying to make a little game in the Console App and i want an enemy to move at a constantly while moving the player. The code looks like this right now:</p>

```<code>public static int y = 5;
    public static string player = "O";
    public static int enemyX = 10;
    static void Main(string[] args)
    {

        while (true)
        {
            Console.SetCursorPosition(enemyX, 10);
            Console.Write("X");
            enemyX = enemyX - 1;
            Console.CursorVisible = false;
            Console.SetCursorPosition(5, y);
            Console.Write(player);

            var move = Console.ReadKey(true);
            switch (move.Key)
            {
                case ConsoleKey.W:
                    y = y - 1;
                    break;
                case ConsoleKey.S:
                    y = y + 1;
                    break;
            }
            if (y <= 0)
            {
                y = 1;
            }

            if (y >= 25)
            {
                y = 24;
            }
            Console.Clear();
        }
    }
</code>```

答:
From what I understand you want the enemy to move regardless of if the player moves or not and to do that you could use Threads
Example:

```<code>bool BreakThread = false; //you need this to break the thread loop
Thread enemyThread = new Thread(()=>
{
  while(!BreakThread)
  {
  //do Enemy Actions
  }
});

enemyThread.Start();
//then execute your main game loop
</code>```

but remember to set the BreakThread value to true when ever you
are closing the game.

Body:
I'm trying to make a little game in the Console App and i want an enemy to move at a constantly while moving the player. The code looks like this right now:

public static int y = 5;
    public static string player = "O";
    public static int enemyX = 10;
    static void Main(string[] args)
    {

        while (true)
        {
            Console.SetCursorPosition(enemyX, 10);
            Console.Write("X");
            enemyX = enemyX - 1;
            Console.CursorVisible = false;
            Console.SetCursorPosition(5, y);
            Console.Write(player);

            var move = Console.ReadKey(true);
            switch (move.Key)
            {
                case ConsoleKey.W:
                    y = y - 1;
                    break;
                case ConsoleKey.S:
                    y = y + 1;
                    break;
            }
            if (y <= 0)
            {
                y = 1;
            }

            if (y >= 25)
            {
                y = 24;
            }
            Console.Clear();
        }
    }

Body:
From what I understand you want the enemy to move regardless of if the player moves or not and to do that you could use Threads
Example:

bool BreakThread = false; //you need this to break the thread loop
Thread enemyThread = new Thread(()=>
{
  while(!BreakThread)
  {
  //do Enemy Actions
  }
});

enemyThread.Start();
//then execute your main game loop

but remember to set the BreakThread value to true when ever you
are closing the game.


问:
dplyr() grouping and getting counts - error message Evaluation error: no applicable method for 'summarise_' applied to an object of class "logical"
I've got a data frame (df) with two variables, location and weather.

I'd like a  wide data frame (dfgoal), in which the data is grouped by location and in which there are three new variables (weather_1 to weather_3) with counts for the observations in the original weather variable.

The problem's that when I try to use dplyr()::mutate() I only get TRUE/FALSE output rather than counts, alternatively an error message: Evaluation error: no applicable method for 'summarise_' applied to an object of class "logical".

Any help would be much appreciated.

Starting point (df):

```<code>df <- data.frame(location=c("az","az","az","az","bi","bi","bi","ca","ca","ca","ca","ca"),weather=c(1,1,2,3,2,3,2,1,2,3,1,2))
</code>```

Desired outcome (df):

```<code>dfgoal <- data.frame(location=c("az","bi","ca"),weather_1=c(2,0,2),weather_2=c(1,2,2),weather_3=c(1,1,1))
</code>```

Current code:

```<code>library(dplyr)
df %>% group_by(location)  %>% mutate(weather_1 = (weather == 1)) %>% mutate(weather_2 = (weather == 2)) %>% mutate(weather_3 = (weather == 3))
df %>% group_by(location)  %>% mutate(weather_1 = summarise(weather == 1)) %>% mutate(weather_2 = summarise(weather == 2)) %>% mutate(weather_3 = summarise(weather == 3))
</code>```

答:
Krzysztof's solution is the way to go, but if you insist on using tidyverse, here is a solution with dplyr + tidyr:

```<code>library(dplyr)
library(tidyr)

df %>%
  group_by(location, weather) %>%
  summarize(count = count(weather)) %>%
  spread(weather, count, sep="_") %>%
  mutate_all(funs(coalesce(., 0L)))
</code>```

Result:

```<code># A tibble: 3 x 4
# Groups:   location [3]
  location weather_1 weather_2 weather_3
    <fctr>     <int>     <int>     <int>
1       az         2         1         1
2       bi         0         2         1
3       ca         2         2         1
</code>```

Body:
I've got a data frame (df) with two variables, location and weather.

I'd like a  wide data frame (dfgoal), in which the data is grouped by location and in which there are three new variables (weather_1 to weather_3) with counts for the observations in the original weather variable.

The problem's that when I try to use dplyr()::mutate() I only get TRUE/FALSE output rather than counts, alternatively an error message: Evaluation error: no applicable method for 'summarise_' applied to an object of class "logical".

Any help would be much appreciated.

Starting point (df):

df <- data.frame(location=c("az","az","az","az","bi","bi","bi","ca","ca","ca","ca","ca"),weather=c(1,1,2,3,2,3,2,1,2,3,1,2))

Desired outcome (df):

dfgoal <- data.frame(location=c("az","bi","ca"),weather_1=c(2,0,2),weather_2=c(1,2,2),weather_3=c(1,1,1))

Current code:

library(dplyr)
df %>% group_by(location)  %>% mutate(weather_1 = (weather == 1)) %>% mutate(weather_2 = (weather == 2)) %>% mutate(weather_3 = (weather == 3))
df %>% group_by(location)  %>% mutate(weather_1 = summarise(weather == 1)) %>% mutate(weather_2 = summarise(weather == 2)) %>% mutate(weather_3 = summarise(weather == 3))

Body:
Krzysztof's solution is the way to go, but if you insist on using tidyverse, here is a solution with dplyr + tidyr:

library(dplyr)
library(tidyr)

df %>%
  group_by(location, weather) %>%
  summarize(count = count(weather)) %>%
  spread(weather, count, sep="_") %>%
  mutate_all(funs(coalesce(., 0L)))

Result:

# A tibble: 3 x 4
# Groups:   location [3]
  location weather_1 weather_2 weather_3
    <fctr>     <int>     <int>     <int>
1       az         2         1         1
2       bi         0         2         1
3       ca         2         2         1


问:
How can I make Gitlab runner merge code into a branch on a successful build
Well the title is pretty much self-explanatory.

In summary, I want a branch (i.e. dev) to be merged to another branch (i.e. production) IF the build is successful.

答:
I tried @jakub-kania solution but I was always getting id_rsa invalid format. I think that gitlab secret variables are screwed somehow.

I made it working by directly passing the deployment key into ssh-add without creating ssh keys. Here is working solution:

```<code>merge to master:
  stage: deploy
  image: alpine
  only:
    - dev-branch
  before_script:
    - apk add --update git openssh-client
    - mkdir ~/.ssh
    - ssh-keyscan -p 2222 <gitlab.domain.com> > ~/.ssh/known_hosts
    - eval `ssh-agent -s`
    - ssh-add <(echo "$GITLAB_DEPLOY_KEY")
    - ssh -T git@<gitlab.domain.com> -p 2222
    - git config --global user.email "$GITLAB_USER_EMAIL"
    - git config --global user.name "$GITLAB_USER_ID"
    - git remote set-url origin ssh://git@<gitlab.domain.com>:2222/path/to/repo.git
  script:
    - git checkout master
    - git reset --hard origin/master
    - git merge $CI_BUILD_REF
    - git push origin master
</code>```

ContentLicense: CC BY-SA 4.0

Body:
I tried @jakub-kania solution but I was always getting id_rsa invalid format. I think that gitlab secret variables are screwed somehow.

I made it working by directly passing the deployment key into ssh-add without creating ssh keys. Here is working solution:

merge to master:
  stage: deploy
  image: alpine
  only:
    - dev-branch
  before_script:
    - apk add --update git openssh-client
    - mkdir ~/.ssh
    - ssh-keyscan -p 2222 <gitlab.domain.com> > ~/.ssh/known_hosts
    - eval `ssh-agent -s`
    - ssh-add <(echo "$GITLAB_DEPLOY_KEY")
    - ssh -T git@<gitlab.domain.com> -p 2222
    - git config --global user.email "$GITLAB_USER_EMAIL"
    - git config --global user.name "$GITLAB_USER_ID"
    - git remote set-url origin ssh://git@<gitlab.domain.com>:2222/path/to/repo.git
  script:
    - git checkout master
    - git reset --hard origin/master
    - git merge $CI_BUILD_REF
    - git push origin master


问:
Can I call Java from Node.js via JNI and how?
can I call Java from Node.js via JNI? Are there any examples?

答:
That looks tricky. Node.JS runs on the Google Chrome JavaScript engine V8. What you will have to do is to create a V8 C++ binding (v8 c++ Crash Course shows an example) that starts a JVM and does all the JNI handling.

I think you might be better off letting a JavaServer and Node.js communicate via the network (someone wrote an example for using RabbitMQ for Java/Node.js message based communication). Here, JSON would be a great data exchange format (if you trust your Java server produces proper JSON you can just eval() it in Node).


问:
create a HTMLCollection
I'm trying to shim Element.prototype.children which should return a HTMLCollection

There is a window.HTMLCollection

However

```<code>var h = new HTMLCollection();
//TypeErrror: HTMLCollection is not a constructor
</code>```

and

```<code>var h = Object.create(HTMLCollection.prototype);
h[0] = div;
h.item(0);
// Could not convert JavaScript argument
</code>```

Test Firefox 7 and Chrome

Apart from shimming HTMLCollection is there any way to interact with it?

Also provide feedback on this github issue if you can suggest a solution

答:
I know this is an older question but I came across a similar need to create an empty HTMLCollection and I did it by simply creating an element and then running getElementsByClassName() against it using a class that doesn't exist in the element.

```<code>document.createElement("div").getElementsByClassName('noClassHere');
</code>```

This returns an empty HTMLCollection object.

Title: create a HTMLCollection

Body:
I'm trying to shim Element.prototype.children which should return a HTMLCollection

There is a window.HTMLCollection

However

var h = new HTMLCollection();
//TypeErrror: HTMLCollection is not a constructor

and

var h = Object.create(HTMLCollection.prototype);
h[0] = div;
h.item(0);
// Could not convert JavaScript argument

Test Firefox 7 and Chrome

Apart from shimming HTMLCollection is there any way to interact with it?

Also provide feedback on this github issue if you can suggest a solution

Body:
I know this is an older question but I came across a similar need to create an empty HTMLCollection and I did it by simply creating an element and then running getElementsByClassName() against it using a class that doesn't exist in the element.

document.createElement("div").getElementsByClassName('noClassHere');

This returns an empty HTMLCollection object.


问:
Interfaces casting dilema
I have an awkward problem with this (propably) simple casting example. Could you please help me?

```<code>public class Example1 {

interface ParentIf{}
interface ChildIf extends ParentIf {}
interface OtherIf {}

class ParentCl {}
class ChildCl extends ParentCl {}
class OtherCl {}

    public static void main(String[] args) {
        ChildIf cI = null;
        ParentIf pI = null;
        OtherIf oI = null;
        ChildCl cC = null;
        ParentCl pC = null;
        OtherCl oC = null;

        cI = (ChildIf)oI; //case1 - fine

        cC = (ChildCl)oC; //case2 - inconvertible types

        cI = (ChildIf)oC; //case3 - fine
    }
}
</code>```

But more awkward is that I don't know why the other two statements are fine.

I can't see any connection between OtherIf and ChildIf. So how is possible to cast the OtherIf to the ChildIf when there is no "extend" between these two interfaces in case1?

答:
```<code>cI = (ChildIf)oI;
</code>```

is fine because oI could be an instance of a class that implements both ChildIf and OtherIf.

```<code>cI = (ChildIf)oC;
</code>```

is fine because oC could be an instance of a class that extends OtherClass anec implements ChildIf.

Title: Interfaces casting dilema

Body:
I have an awkward problem with this (propably) simple casting example. Could you please help me?

public class Example1 {

interface ParentIf{}
interface ChildIf extends ParentIf {}
interface OtherIf {}

class ParentCl {}
class ChildCl extends ParentCl {}
class OtherCl {}

    public static void main(String[] args) {
        ChildIf cI = null;
        ParentIf pI = null;
        OtherIf oI = null;
        ChildCl cC = null;
        ParentCl pC = null;
        OtherCl oC = null;

        cI = (ChildIf)oI; //case1 - fine

        cC = (ChildCl)oC; //case2 - inconvertible types

        cI = (ChildIf)oC; //case3 - fine
    }
}

But more awkward is that I don't know why the other two statements are fine.

I can't see any connection between OtherIf and ChildIf. So how is possible to cast the OtherIf to the ChildIf when there is no "extend" between these two interfaces in case1?

Body:
cI = (ChildIf)oI;

is fine because oI could be an instance of a class that implements both ChildIf and OtherIf.

cI = (ChildIf)oC;

is fine because oC could be an instance of a class that extends OtherClass anec implements ChildIf.


问:
Arduino: catching events from camera via USB
I'm working on an arduino project that needs to communicate via USB with cameras. There is a great library that everyone knows of when it comes to arduino and USB: https://github.com/felis/PTP_2.0

There are lots of examples on how to use it and even examples of how to communicate with Canon cameras. Even some sort of event handling has its example on github!

But for a VERY long time I just don't get it done: catching the event when a photo was taken!

I digged into the EOSEventLab example and all the other examples, searched the internet up and down but I can't find an answer to my problem. Everyone seems to use "delay" to trigger code after capturing... I can't be the only one in need for this. :)

The following steps should be done:

setup camera parameters and max. number of images (done)

take a picture (done)

execute some more code when camera is ready again aka image is captured

repeat from 2. until max. number of images are captured

I can give some code if needed but mainly it's the code found in the EOSEventLab example.

Thank you very much for your help!

答:
YES!!!

Okay, after months of crawling through the code (and maybe because of switching from the arduino IDE to Visual Studio == better code highlighting, code completion...) I found the answer. It was sitting directly in front of my face!

It's inside the header an cpp files on https://github.com/felis/PTP_2.0/tree/master/examples/Canon_EOS/EOSRemote

One has to add the method "OnObjectCreated" to eoseventhandlers.h

```<code>virtual void OnObjectCreated(const EOSEvent *evt);
</code>```

and implement this method in eoseventhandlers.cpp

```<code>void EosEventHandlers::OnObjectCreated(const EOSEvent *evt)
{
    E_Notify(PSTR("capturing done!"), 0x80);
}
</code>```

The rest is up to you! "Object Created" is kind of a synonym for "Capturing done". If I understand it correctly you get the address to the image (object) using evt->propCode inside this method. I didn't try to use that information for filename extraction and so on but it may be useful for that.

Although this answer is great, it doesn't apply to all events the camera triggers but "only" the event I was looking for. It's great to be able to wait for a photo to be captured instead of using delays.

Body:
YES!!!

Okay, after months of crawling through the code (and maybe because of switching from the arduino IDE to Visual Studio == better code highlighting, code completion...) I found the answer. It was sitting directly in front of my face!

It's inside the header an cpp files on https://github.com/felis/PTP_2.0/tree/master/examples/Canon_EOS/EOSRemote

One has to add the method "OnObjectCreated" to eoseventhandlers.h

virtual void OnObjectCreated(const EOSEvent *evt);

and implement this method in eoseventhandlers.cpp

void EosEventHandlers::OnObjectCreated(const EOSEvent *evt)
{
    E_Notify(PSTR("capturing done!"), 0x80);
}

The rest is up to you! "Object Created" is kind of a synonym for "Capturing done". If I understand it correctly you get the address to the image (object) using evt->propCode inside this method. I didn't try to use that information for filename extraction and so on but it may be useful for that.

Although this answer is great, it doesn't apply to all events the camera triggers but "only" the event I was looking for. It's great to be able to wait for a photo to be captured instead of using delays.


问:
Visual Studio Extensions SDK - can we access FTP functions?
I am trying to build a Visual Studio extension (using VS 2017 Community).

Visual Studio already comes with the ability to publish files via FTP to a remote server (for website projects); so obviously, there must be some sort of FTP library (or class) within Visual Studio.

My Question:

Does anyone know if there is an FTP class within the Visual Studio SDK that allows developers to publish files via FTP?  Currently, I have not found anything within the Visual Studio Extensions SDK documentation -- but I thought I should ask those who may be in the know.

Thanks in advance.

答:
Microsoft Visual Studio uses a licensed copy of Rebex FTP/SSL client library,

https://www.rebex.net/ftp-ssl.net/default.aspx

which can be found in path such as C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Rebex.Net.Ftp.dll

Whether you can consume that library in your extension is not clear. So if you do want to be freely working on your extension, use an open source FTP library or the .NET Framework built-in classes might be a better option.


问:
memcpy vs for loop - What's the proper way to copy an array from a pointer?
I have a function foo(int[] nums) which I understand is essentially equivalent to foo(int* nums).  Inside foo I need to copy the contents of the array pointed to by numsinto some int[10] declared within the scope of foo.  I understand the following is invalid:

```<code>void foo (int[] nums)
{
    myGlobalArray = *nums
}
</code>```

What is the proper way to copy the array?  Should I use memcpy like so:

```<code>void foo (int[] nums)
{
    memcpy(&myGlobalArray, nums, 10);
}
</code>```

or should I use a for loop?

```<code>void foo(int[] nums)
{
    for(int i =0; i < 10; i++)
    {
        myGlobalArray[i] = nums[i];
    }
}
</code>```

Is there a third option that I'm missing?

答:
Essentially, as long as you are dealing with POD types (Plain Ol' Data), such as int, unsigned int, pointers, data-only structs, etc... you are safe to use mem*.

If your array contains objects, use the for loop, as the = operator may be required to ensure proper assignment.

Body:
I have a function foo(int[] nums) which I understand is essentially equivalent to foo(int* nums).  Inside foo I need to copy the contents of the array pointed to by numsinto some int[10] declared within the scope of foo.  I understand the following is invalid:

void foo (int[] nums)
{
    myGlobalArray = *nums
}

What is the proper way to copy the array?  Should I use memcpy like so:

void foo (int[] nums)
{
    memcpy(&myGlobalArray, nums, 10);
}

or should I use a for loop?

void foo(int[] nums)
{
    for(int i =0; i < 10; i++)
    {
        myGlobalArray[i] = nums[i];
    }
}

Is there a third option that I'm missing?


问:
how to do Seq.takeWhile + one item in F#
I would like to write a function which filters a sequence using a predicate but the result should also INCLUDE the first item for which the predicate returns false.

The logic would be something like this, if there was a break keyword in F#

```<code>let myFilter predicate s =
    seq {
        for item in s do
            yield item
            if predicate item then
                break
    }
</code>```

I tried combinations of Seq.takeWhile and Seq.skipWhile, something like this:

```<code>Seq.append
    (Seq.takeWhile predicate s)
    (Seq.skipWhile predicate s |> Seq.take 1)
</code>```

...but the problem is that the first item which matches the predicate is lost between the takeWhile and the skipWhile

Also note that the input sequence is lazy so any solution which consumes the sequence and takes decisions afterwards is not viable.

Any ideas?

Thanks!

EDIT: Thanks a LOT for all the answers! I didn't expect so many responses so fast. I will take a look at each of them soon. Now I just want to give a little more context. Consider the following coding kata which implements a shell:

```<code>let cmdProcessor state = function
    | "q" -> "Good bye!"
    | "h" -> "Help content"
    | c -> sprintf "Bad command: '%s'" c

let processUntilQuit =
    Seq.takeWhile (fun cmd -> cmd <> "q")

let processor =
    processUntilQuit
    >> Seq.scan cmdProcessor "Welcome!"

module io =
    let consoleLines = seq { while true do yield System.Console.ReadLine () }

    let display : string seq -> unit = Seq.iter <| printfn "%s"

io.consoleLines |> processor|> io.display

printf "Press any key to continue..."
System.Console.ReadKey ()|> ignore
</code>```

This implementation has the trouble that it doesn't print "Good bye!" when command q is entered.

What I want to do is to implement the function processUntilQuit such that it processes all the commands until "q", including "q".

答:
This is very old but thought I'd contribute because the other solutions did not suggest this...

What about using Seq.scan to establish a two element stack of predicate results and simply take while the bottom of that stack, representing the previous element's predicate result, is true? (note, haven't tested this code)

```<code>Seq.scan (fun (a,b,v) e -> (pred e, a, Some e)) (true, true, None )
>> Seq.takeWhile (fun (_,b,_) -> b)
>> Seq.map (fun (_,_,c) -> c)
</code>```

Body:
I would like to write a function which filters a sequence using a predicate but the result should also INCLUDE the first item for which the predicate returns false.

The logic would be something like this, if there was a break keyword in F#

let myFilter predicate s =
    seq {
        for item in s do
            yield item
            if predicate item then
                break
    }

I tried combinations of Seq.takeWhile and Seq.skipWhile, something like this:

Seq.append
    (Seq.takeWhile predicate s)
    (Seq.skipWhile predicate s |> Seq.take 1)

...but the problem is that the first item which matches the predicate is lost between the takeWhile and the skipWhile

Also note that the input sequence is lazy so any solution which consumes the sequence and takes decisions afterwards is not viable.

Any ideas?

Thanks!

EDIT: Thanks a LOT for all the answers! I didn't expect so many responses so fast. I will take a look at each of them soon. Now I just want to give a little more context. Consider the following coding kata which implements a shell:

let cmdProcessor state = function
    | "q" -> "Good bye!"
    | "h" -> "Help content"
    | c -> sprintf "Bad command: '%s'" c

let processUntilQuit =
    Seq.takeWhile (fun cmd -> cmd <> "q")

let processor =
    processUntilQuit
    >> Seq.scan cmdProcessor "Welcome!"

module io =
    let consoleLines = seq { while true do yield System.Console.ReadLine () }

    let display : string seq -> unit = Seq.iter <| printfn "%s"

io.consoleLines |> processor|> io.display

printf "Press any key to continue..."
System.Console.ReadKey ()|> ignore

This implementation has the trouble that it doesn't print "Good bye!" when command q is entered.

What I want to do is to implement the function processUntilQuit such that it processes all the commands until "q", including "q".

Body:
This is very old but thought I'd contribute because the other solutions did not suggest this...

What about using Seq.scan to establish a two element stack of predicate results and simply take while the bottom of that stack, representing the previous element's predicate result, is true? (note, haven't tested this code)

Seq.scan (fun (a,b,v) e -> (pred e, a, Some e)) (true, true, None )
>> Seq.takeWhile (fun (_,b,_) -> b)
>> Seq.map (fun (_,_,c) -> c)


问:
Behavior of const auto pointers in C++
This answer claims that a const auto pointer behaves the same as a const 'regular' (i.e., non-auto) pointer, which is what I would expect.

However, the following code compiles and outputs 100:

```<code>int n{ 99 };
const auto nPtr = &n;
++(*nPtr);
std::cout << n << '\n';
</code>```

To dig a little deeper, I checked the types of all 4 kinds of pointers and this is the result I got:

Code

```<code>int n{ 99 };

int* intPtr = &n;
const int* intConstPtr = &n;

auto autoPtr = &n;
const auto autoConstPtr = &n;

std::cout << "intPtr: " << typeid(intPtr).name() << '\n';
std::cout << "intConstPtr: " << typeid(intConstPtr).name() << '\n';

std::cout << "autoPtr: " << typeid(autoPtr).name() << '\n';
std::cout << "autoConstPtr: " << typeid(autoConstPtr).name() << '\n';
</code>```

Output

intPtr: int * __ptr64

intConstPtr: int const * __ptr64

autoPtr: int * __ptr64

autoConstPtr: int * __ptr64

So the compiler seems to be completely ignoring the const keyword with the auto pointer. Does anyone know why this is?

答:
When you write const auto, you are saying: make the type of deduced variable, const. The type of the deduced variable is int *, that, is, pointer-to-int. So you are saying: a const variable, of type pointer-to-int. Or, more formally: int * const. The type of the pointee is still just int, non-const-qualified, so modifying is no problem. You just can't change what the pointer is pointing to. If you want to control the const-ness of the pointed to type, you can't just use auto because there's no awareness that it is specifically deducing to a pointer type. So you would have to write:

```<code>const auto * intConstPtr = &n;
</code>```

The auto here is just deducing to the pointee (int), which is what gets qualified by the const. So this deduces to: pointer-to-const-int. You could also write:

```<code>const auto * const intConstPtr = &n;
</code>```

For type const-pointer-to-const-int. Though, at that point, you should probably just use a reference. This is why const pointers (as opposed to pointers-to-const) aren't all that common in C++, at least in some codebases (there are certainly exceptions).

Title: Behavior of const auto pointers in C++

Body:
This answer claims that a const auto pointer behaves the same as a const 'regular' (i.e., non-auto) pointer, which is what I would expect.

However, the following code compiles and outputs 100:

int n{ 99 };
const auto nPtr = &n;
++(*nPtr);
std::cout << n << '\n';

To dig a little deeper, I checked the types of all 4 kinds of pointers and this is the result I got:

Code

int n{ 99 };

int* intPtr = &n;
const int* intConstPtr = &n;

auto autoPtr = &n;
const auto autoConstPtr = &n;

std::cout << "intPtr: " << typeid(intPtr).name() << '\n';
std::cout << "intConstPtr: " << typeid(intConstPtr).name() << '\n';

std::cout << "autoPtr: " << typeid(autoPtr).name() << '\n';
std::cout << "autoConstPtr: " << typeid(autoConstPtr).name() << '\n';

Output

intPtr: int * __ptr64

intConstPtr: int const * __ptr64

autoPtr: int * __ptr64

autoConstPtr: int * __ptr64

So the compiler seems to be completely ignoring the const keyword with the auto pointer. Does anyone know why this is?

Body:
When you write const auto, you are saying: make the type of deduced variable, const. The type of the deduced variable is int *, that, is, pointer-to-int. So you are saying: a const variable, of type pointer-to-int. Or, more formally: int * const. The type of the pointee is still just int, non-const-qualified, so modifying is no problem. You just can't change what the pointer is pointing to. If you want to control the const-ness of the pointed to type, you can't just use auto because there's no awareness that it is specifically deducing to a pointer type. So you would have to write:

const auto * intConstPtr = &n;

The auto here is just deducing to the pointee (int), which is what gets qualified by the const. So this deduces to: pointer-to-const-int. You could also write:

const auto * const intConstPtr = &n;

For type const-pointer-to-const-int. Though, at that point, you should probably just use a reference. This is why const pointers (as opposed to pointers-to-const) aren't all that common in C++, at least in some codebases (there are certainly exceptions).


问:
Problems with generics and static methods - Java
I'm trying my hand at Generics and implementing a generic list in a simple text based UI, however I keep getting messages saying that I can't get to different methods etc from my methods that are and aren't static.

Can somebody please point out what I am doing wrong?

```<code>public class GenericList<T> {

    List<T> genericList = new ArrayList<T>();

    public void addGeneric(T t) {
        this.genericList.add(t);
    }

    public static void initial() {
        // USER INPUT AREA
        switch(ans.toLowerCase()) {
            case "add" : addToList(); break;
            case "delete" : deleteFromList(); break;
            case "print" : printList(); break;
        }
    }

    public void deleteFromList() {}

    public void printList() {
        for (int i = 0; i<this.genericList.size(); i++) {
            System.out.println(this.genericList.get(i));
        }
    }

    public void addToList() {
        // USER INPUT AREA
        switch(type.toLowerCase()) {
            case "x":
                System.out.println("Exitiing...");
                initial();
                break;
            case "string": type="String"; break;
            case "int": type="Integer"; break;
            case "integer": type="Integer"; break;
        }

        System.out.println("Enter data...");
        String data = s.next();
        if (data.equals("x")){
            System.out.println("Exiting...");
            initial();
        }
        initial();

    }

    public static void main(String[] args) {
        GenericList g = new GenericList();
        initial();

    }
}
</code>```

答:
Your problem isn't generics. Change

```<code>public static void initial() {
</code>```

to

```<code>public void initial() {
</code>```

and change yout main to

```<code>public static void main(String[] args) {
    new GenericList().initial();
}
</code>```

Body:
I'm trying my hand at Generics and implementing a generic list in a simple text based UI, however I keep getting messages saying that I can't get to different methods etc from my methods that are and aren't static.

Can somebody please point out what I am doing wrong?

public class GenericList<T> {

    List<T> genericList = new ArrayList<T>();

    public void addGeneric(T t) {
        this.genericList.add(t);
    }

    public static void initial() {
        // USER INPUT AREA
        switch(ans.toLowerCase()) {
            case "add" : addToList(); break;
            case "delete" : deleteFromList(); break;
            case "print" : printList(); break;
        }
    }

    public void deleteFromList() {}

    public void printList() {
        for (int i = 0; i<this.genericList.size(); i++) {
            System.out.println(this.genericList.get(i));
        }
    }

    public void addToList() {
        // USER INPUT AREA
        switch(type.toLowerCase()) {
            case "x":
                System.out.println("Exitiing...");
                initial();
                break;
            case "string": type="String"; break;
            case "int": type="Integer"; break;
            case "integer": type="Integer"; break;
        }

        System.out.println("Enter data...");
        String data = s.next();
        if (data.equals("x")){
            System.out.println("Exiting...");
            initial();
        }
        initial();

    }

    public static void main(String[] args) {
        GenericList g = new GenericList();
        initial();

    }
}

Body:
Your problem isn't generics. Change

public static void initial() {

to

public void initial() {

and change yout main to

public static void main(String[] args) {
    new GenericList().initial();
}


问:
Data Versioning in Cassandra with CQL3
I am quite a n00b in Cassandra (I'm mainly from an RDBMS background with some NoSQL here and there, like Google's BigTable and MongoDB) and I'm struggling with the data modelling for the use cases I'm trying to satisfy.  I looked at this and this and even this but they're not exactly what I needed.

I have this basic table:

```<code>CREATE TABLE documents (
    itemid_version text,
    xml_payload text,
    insert_time timestamp,
    PRIMARY KEY (itemid_version)
);
</code>```

itemid is actually a UUID (and unique for all documents), and version is an int (version 0 is the "first" version).  xml_payload is the full XML doc, and can get quite big.  Yes, I'm essentially creating a versioned document store.

As you can see, I concatenated the two to create a primary key and I'll get to why I did this later as I explain the requirements and/or use cases:

user needs to get the single (1) doc he wants, he knows the item id and version (not necessarily the latest)

user needs to get the single (1) doc he wants, he knows the item id but does not know the latest version

user needs the version history of a single (1) doc.

user needs to get the list (1 or more) of docs he wants, he knows the item id AND version (not necessarily the latest)

I will be writing the client code that will perform the use cases, please excuse the syntax as I'm trying to be language-agnostic

first one's straightforward:

```<code>$itemid_version = concat($itemid, $version)
$doc = csql("select * from documents where itemid_version = {0};"
    -f $itemid_version)
</code>```

now to satisfy the 2nd and 3rd use cases, I am adding the following table:

```<code>CREATE TABLE document_versions (
    itemid uuid,
    version int,
    PRIMARY KEY (itemid, version)
) WITH clustering order by (version DESC);
</code>```

new records will be added as new docs and new versions of existing docs are created

now we have this (use case #2):

```<code>$latest_itemid, $latest_version = csql("select itemid,
    version from document_versions where item_id = {0}
    order by version DESC limit 1;" -f $itemid)
$itemid_version = concat($latest_itemid, $latest_version)
$doc = csql("select * from documents where itemid_version = {0};"
    -f $itemid_version)
</code>```

and this (use case #3):

```<code>$versions = csql("select version from document_versions where item_id = {0}"
    -f $itemid)
</code>```

for the 3rd requirement, I am adding yet another table:

```<code>CREATE TABLE latest_documents (
    itemid uuid,
    version int,
    PRIMARY KEY (itemid, version)
)
</code>```

records are inserted for new docs, records are updated for existing docs

and now we have this:

```<code>$latest_itemids, $latest_versions = csql("select itemid, version
    from latest_documents where item_id in ({0})" -f $itemid_list.toCSV())

foreach ($one_itemid in $latest_itemids, $one_version in $latest_versions)
    $itemid_version = concat($latest_itemid, $latest_version)
    $latest_docs.append(
        cql("select * from documents where itemid_version = {0};"
        -f $itemid_version))
</code>```

Now I hope it's clear why I concatenated itemid and version to create an index for documents as opposed to creating a compound key: I cannot have OR in the WHERE clause in SELECT

You can assume that only one process will do the inserts/updates so you don't need to worry about consistency or isolation issues.

Am I on the right track here?  there are quite a number of things that doesn't sit well with me...but mainly because I don't understand Cassandra yet:

I feel that the primary key for documents should be a composite of (itemid, version) but I can't satisfy use case #4 (return a list from a query)...I can't possibly use a separate SELECT statement for each document due to the performance hit (network overhead)...or can (should) I?

2 trips to get a document if the version is not known beforehand.  probably a compromise I have to live with, or maybe there's a better way.

答:
Lets see if we can come up with a model in a top down fashion starting from your queries:

CREATE TABLE document_versions (
  itemid uuid,
  name text STATIC,
  vewrsion int,
  xml_payload text,
  insert_time timestamp,
  PRIMARY KEY ((itemid), version)
) WITH CLUSTERING ORDER BY (version DESC);
```

<ol>
<li><p>Use case 1: user needs to get the single (1) doc he wants, he knows the item id and version (not necessarily the latest)</p>

<pre class="lang-sql prettyprint-override"><code>SELECT * FROM document_versions
  WHERE itemid = ? and version = ?;
</code>```

Use case 2: user needs to get the single (1) doc he wants, he knows the item id but does not know the latest version

SELECT * FROM document_versions
  WHERE itemid = ? limit 1;
```</li>
<li><p>Use case 3: user needs the version history of a single (1) doc.</p>

<pre class="lang-sql prettyprint-override"><code>SELECT * FROM document_versions
  WHERE itemid = ?
</code>```

Use case 4: user needs to get the list (1 or more) of docs he wants, he knows the item id AND version (not necessarily the latest)

SELECT * FROM documents
  WHERE itemid = 'doc1' and version IN ('1', '2');
```

One table for all these queries is the correct approach.  I would suggest taking the Datastax free online course: DS220 Data Modeling

Title: Data Versioning in Cassandra with CQL3

Body:
I am quite a n00b in Cassandra (I'm mainly from an RDBMS background with some NoSQL here and there, like Google's BigTable and MongoDB) and I'm struggling with the data modelling for the use cases I'm trying to satisfy.  I looked at this and this and even this but they're not exactly what I needed.

I have this basic table:

CREATE TABLE documents (
    itemid_version text,
    xml_payload text,
    insert_time timestamp,
    PRIMARY KEY (itemid_version)
);

itemid is actually a UUID (and unique for all documents), and version is an int (version 0 is the "first" version).  xml_payload is the full XML doc, and can get quite big.  Yes, I'm essentially creating a versioned document store.

As you can see, I concatenated the two to create a primary key and I'll get to why I did this later as I explain the requirements and/or use cases:

user needs to get the single (1) doc he wants, he knows the item id and version (not necessarily the latest)

user needs to get the single (1) doc he wants, he knows the item id but does not know the latest version

user needs the version history of a single (1) doc.

user needs to get the list (1 or more) of docs he wants, he knows the item id AND version (not necessarily the latest)

I will be writing the client code that will perform the use cases, please excuse the syntax as I'm trying to be language-agnostic

first one's straightforward:

$itemid_version = concat($itemid, $version)
$doc = csql("select * from documents where itemid_version = {0};"
    -f $itemid_version)

now to satisfy the 2nd and 3rd use cases, I am adding the following table:

CREATE TABLE document_versions (
    itemid uuid,
    version int,
    PRIMARY KEY (itemid, version)
) WITH clustering order by (version DESC);

new records will be added as new docs and new versions of existing docs are created

now we have this (use case #2):

$latest_itemid, $latest_version = csql("select itemid,
    version from document_versions where item_id = {0}
    order by version DESC limit 1;" -f $itemid)
$itemid_version = concat($latest_itemid, $latest_version)
$doc = csql("select * from documents where itemid_version = {0};"
    -f $itemid_version)

and this (use case #3):

$versions = csql("select version from document_versions where item_id = {0}"
    -f $itemid)

for the 3rd requirement, I am adding yet another table:

CREATE TABLE latest_documents (
    itemid uuid,
    version int,
    PRIMARY KEY (itemid, version)
)

records are inserted for new docs, records are updated for existing docs

and now we have this:

$latest_itemids, $latest_versions = csql("select itemid, version
    from latest_documents where item_id in ({0})" -f $itemid_list.toCSV())

foreach ($one_itemid in $latest_itemids, $one_version in $latest_versions)
    $itemid_version = concat($latest_itemid, $latest_version)
    $latest_docs.append(
        cql("select * from documents where itemid_version = {0};"
        -f $itemid_version))

Now I hope it's clear why I concatenated itemid and version to create an index for documents as opposed to creating a compound key: I cannot have OR in the WHERE clause in SELECT

You can assume that only one process will do the inserts/updates so you don't need to worry about consistency or isolation issues.

Am I on the right track here?  there are quite a number of things that doesn't sit well with me...but mainly because I don't understand Cassandra yet:

I feel that the primary key for documents should be a composite of (itemid, version) but I can't satisfy use case #4 (return a list from a query)...I can't possibly use a separate SELECT statement for each document due to the performance hit (network overhead)...or can (should) I?

2 trips to get a document if the version is not known beforehand.  probably a compromise I have to live with, or maybe there's a better way.

Body:
Lets see if we can come up with a model in a top down fashion starting from your queries:

CREATE TABLE document_versions (
  itemid uuid,
  name text STATIC,
  vewrsion int,
  xml_payload text,
  insert_time timestamp,
  PRIMARY KEY ((itemid), version)
) WITH CLUSTERING ORDER BY (version DESC);

Use case 1: user needs to get the single (1) doc he wants, he knows the item id and version (not necessarily the latest)

SELECT * FROM document_versions
  WHERE itemid = ? and version = ?;

Use case 2: user needs to get the single (1) doc he wants, he knows the item id but does not know the latest version

SELECT * FROM document_versions
  WHERE itemid = ? limit 1;

Use case 3: user needs the version history of a single (1) doc.

SELECT * FROM document_versions
  WHERE itemid = ?

Use case 4: user needs to get the list (1 or more) of docs he wants, he knows the item id AND version (not necessarily the latest)

SELECT * FROM documents
  WHERE itemid = 'doc1' and version IN ('1', '2');

One table for all these queries is the correct approach.  I would suggest taking the Datastax free online course: DS220 Data Modeling


问:
How can I tell that my program is under unit test environment
I have a console application. In release environment, it works perfectly at this time. When in IDE debug environment, I don't want the console window close, so I added this function, and calling it in the very end of my program.

```<code>[Conditional("DEBUG")]
public static void DebugWaitAKey(string message = "Press any key")
{
    Console.WriteLine(message);
    Console.ReadKey();
}
</code>```

It works well for me, when I debug my program. But with unit testing, it still wait for a key before exiting!

The work-around is only unit-testing release edition of my program, or test other functions. But I do want something can identify current session is under unit testing, and use that flag in this function.

答:
I used a variation of Jeff's UnitTestDetector.  I did not want to check the unit test assembly and wanted to control which unit tests would consider this or not.

So I created a simple class with IsInUnitTest defaulting to false.
Then in the unit test classes where I wanted the conditional code to run I added TestInitializer and TestCleanup where I set the bool accordingly.

Then in my regular code I can use UnitTestDetector.IsInUnitTest

Simple UnitTestDetector class:

```<code>/// <summary>
/// Detects if we are running inside a unit test.
/// </summary>
public static class UnitTestDetector
{
    static private bool _isInUnitTest = false;
    public static bool IsInUnitTest
    {
        get { return _isInUnitTest; }
        set { _isInUnitTest = value; }
    }
}
</code>```

Unit Test to Test this stuff:

```<code>[TestClass]
public class UnitTestDetectorTest_WithoutIsInUnitTest
{
    [TestMethod]
    public void IsInUnitTest_WithoutUnitTestAttribute_False()
    {
        bool expected = false;
        bool actual = UnitTestDetector.IsInUnitTest;
        Assert.AreEqual(expected, actual);
    }
}

[TestClass]
public class UnitTestDetectorTest_WithIsInUnitTest
{
    [TestInitialize()]
    public void Initialize()
    {
        UnitTestDetector.IsInUnitTest = true;
    }

    [TestCleanup()]
    public void Cleanup()
    {
        UnitTestDetector.IsInUnitTest = false;
    }

    [TestMethod]
    public void IsInUnitTest_WithUnitTestAttribute_True()
    {
        bool expected = true;
        bool actual = UnitTestDetector.IsInUnitTest;
        Assert.AreEqual(expected, actual);
    }
}
</code>```

Condition in Code:

```<code>if (UnitTestDetector.IsInUnitTest)
            return "Hey I'm in the unit test :)";
</code>```

Body:
I have a console application. In release environment, it works perfectly at this time. When in IDE debug environment, I don't want the console window close, so I added this function, and calling it in the very end of my program.

[Conditional("DEBUG")]
public static void DebugWaitAKey(string message = "Press any key")
{
    Console.WriteLine(message);
    Console.ReadKey();
}

It works well for me, when I debug my program. But with unit testing, it still wait for a key before exiting!

The work-around is only unit-testing release edition of my program, or test other functions. But I do want something can identify current session is under unit testing, and use that flag in this function.

Body:
I used a variation of Jeff's UnitTestDetector.  I did not want to check the unit test assembly and wanted to control which unit tests would consider this or not.

So I created a simple class with IsInUnitTest defaulting to false.
Then in the unit test classes where I wanted the conditional code to run I added TestInitializer and TestCleanup where I set the bool accordingly.

Then in my regular code I can use UnitTestDetector.IsInUnitTest

Simple UnitTestDetector class:

/// <summary>
/// Detects if we are running inside a unit test.
/// </summary>
public static class UnitTestDetector
{
    static private bool _isInUnitTest = false;
    public static bool IsInUnitTest
    {
        get { return _isInUnitTest; }
        set { _isInUnitTest = value; }
    }
}

Unit Test to Test this stuff:

[TestClass]
public class UnitTestDetectorTest_WithoutIsInUnitTest
{
    [TestMethod]
    public void IsInUnitTest_WithoutUnitTestAttribute_False()
    {
        bool expected = false;
        bool actual = UnitTestDetector.IsInUnitTest;
        Assert.AreEqual(expected, actual);
    }
}

[TestClass]
public class UnitTestDetectorTest_WithIsInUnitTest
{
    [TestInitialize()]
    public void Initialize()
    {
        UnitTestDetector.IsInUnitTest = true;
    }

    [TestCleanup()]
    public void Cleanup()
    {
        UnitTestDetector.IsInUnitTest = false;
    }

    [TestMethod]
    public void IsInUnitTest_WithUnitTestAttribute_True()
    {
        bool expected = true;
        bool actual = UnitTestDetector.IsInUnitTest;
        Assert.AreEqual(expected, actual);
    }
}

Condition in Code:

if (UnitTestDetector.IsInUnitTest)
            return "Hey I'm in the unit test :)";