how do I use two pipes sequentially?
<div class="thread" *ngFor="thread of threadlist | bookmarkPipe | threadPipe"></div>
In specific my threads have a bookmark:boolean property as well as tag properties (unit,task,subtask).
So what I want to achieve is that the first pipe filters all threads which are bookmarked, then apply the 2nd pipe (below)
export class ThreadPipe implements PipeTransform{
transform(array:Thread[], [unit,task,subtask]):any{
//See all Threads
if(unit == 0 && task == 0 && subtask == 0){
return array
}
//See selected Units only
if(unit != 0 && task == 0 && subtask == 0){
return array.filter(thread => {
return thread.unit === unit;
});
}
//See selected Units and Tasks
if (unit != 0 && task != 0 && subtask == 0){
return array.filter(thread => {
return thread.unit === unit && thread.task === task;
});
}
// See selected units, tasks, subtask
if (unit != 0 && task != 0 && subtask != 0){
return array.filter(thread => {
return thread.unit === unit && thread.task === task && thread.subtask === subtask;
});
}
}
}
In fact, there is nothing special to do. The second pipe will receive the value from the previous one:
data -> Pipe1 -> filtered data (#1) -> Pipe2 -> filtered data (#2)
For example, if I have an array [ 'val1', 'val2', 'val3' ] and with the following pipes:
#Pipe({
name:'pipe1'
})
export class Pipe1 {
transform(data) {
return data.filter((d) => {
return d!= 'val1';
});
}
}
#Pipe({
name:'pipe2'
})
export class Pipe2 {
transform(data) {
return data.filter((d) => {
return d!= 'val2';
});
}
}
By using the following expression in an ngFor:
#elt of data | pipe1 | pipe2
data | pipe1 will return [ 'val2', 'val3' ] and return [ 'val3' ] if we apply the pipe2 on the previous result.
See the corresponding plunkr: https://plnkr.co/edit/EpKMitR3w89fRiyGYQz7?p=preview.
Related
Any suggestions on how to improve the following code to make it more Functional Programming oriented. Specifically how to remove the MutableList which signifies historical states. There are two data classes: Bank, which represents a riverbank (number of missionaries and number of cannibals currently on the bank) and BankState which represents a historical state of the two banks (the source bank, target bank and boatAtSource - a boolean which indicates whether the boat is currently at the source or target bank). overloaded operator function plus adds missionaries and cannibals to a riverbank and function minus removes them from a riverbank. The boat function is the one which carries the most heft. You can call the following algorithm from fun main (app.kt) as such:
app.kt
fun main(args:Array<String>) {
val source:Bank = Bank(3,3)
val target:Bank = Bank()
source boat target
}
Bank.kt
data class Bank(val missionaries:Int=0,val cannibals:Int=0)
data class BankState(val sourceTarget:Pair<Bank,Bank>,val boatAtSource:Boolean)
operator fun Bank.plus(b:Pair<Int,Int>):Bank = Bank(this.missionaries+b.first,this.cannibals+b.second)
operator fun Bank.minus(b:Pair<Int,Int>):Bank = Bank(this.missionaries-b.first,this.cannibals-b.second)
infix fun Bank.boat(target:Bank):List<BankState> {
val begin = Pair(this,target)
val history = mutableListOf<BankState>(BankState(begin,true))
boat(begin,true,this.missionaries,this.cannibals,history)
return history
}
fun boat(sourceTarget:Pair<Bank,Bank>,
boatAtSource:Boolean,
totalMissionaries:Int,
totalCannibals:Int,
history:MutableList<BankState>):Boolean {
if(sourceTarget.first.cannibals+sourceTarget.second.cannibals==totalCannibals &&
sourceTarget.first.missionaries + sourceTarget.second.missionaries==totalMissionaries &&
sourceTarget.first.cannibals>=0 &&
sourceTarget.first.missionaries>=0 &&
sourceTarget.second.cannibals>=0 &&
sourceTarget.second.missionaries>=0 &&
(sourceTarget.first.missionaries==0 || sourceTarget.first.missionaries>=sourceTarget.first.cannibals) &&
(sourceTarget.second.missionaries==0 || sourceTarget.second.missionaries >= sourceTarget.second.cannibals)) {
if(sourceTarget.second.missionaries==totalMissionaries &&
sourceTarget.second.cannibals==totalCannibals) {
history.forEach(::println)
return true
} else {
val deltas = listOf(Pair(0,1),Pair(1,1),Pair(1,0),Pair(2,0),Pair(0,2))
val comparator = object : Comparator<Pair<Pair<Boolean,Int>,Pair<Bank,Bank>>> {
override fun compare(arg1:Pair<Pair<Boolean,Int>,Pair<Bank,Bank>>,arg2:Pair<Pair<Boolean,Int>,Pair<Bank,Bank>>):Int {
if(arg1.first.first && arg2.first.first) {
return if(arg1.first.second<arg2.first.second) -1 else if(arg1.first.second>arg2.first.second) 1 else 0
} else if(arg1.first.first){
return 1
} else if(arg2.first.first) {
return -1
}
return 0
}
}
val result = deltas.map{
checkNext(it.first,it.second,totalMissionaries,totalCannibals,history,sourceTarget,boatAtSource)
}.maxWith(comparator)
if(result?.first?.first!=null && result.first.first) {
history.add(BankState(result.second,!boatAtSource))
return true;
}
}
}
return false
}
fun checkNext(missionariesDelta:Int,
cannibalsDelta:Int,
totalMissionaries:Int,
totalCannibals:Int,
history:MutableList<BankState>,
sourceTarget:Pair<Bank,Bank>,
boatAtSource:Boolean):Pair<Pair<Boolean,Int>,Pair<Bank,Bank>> {
val nextSrcTgt = if(boatAtSource) Pair(sourceTarget.first-Pair(missionariesDelta,cannibalsDelta),sourceTarget.second+Pair(missionariesDelta,cannibalsDelta))
else Pair(sourceTarget.first+Pair(missionariesDelta,cannibalsDelta),sourceTarget.second-Pair(missionariesDelta,cannibalsDelta))
val bankState:BankState = BankState(nextSrcTgt,!boatAtSource)
if(!history.contains(bankState)) {
history.add(bankState)
val combo2:Boolean = boat(nextSrcTgt,!boatAtSource,totalMissionaries,totalCannibals,history)
val combo2Depth = history.size
history.remove(bankState)
return Pair(Pair(combo2,combo2Depth),nextSrcTgt)
} else {
return Pair(Pair(false,0),nextSrcTgt)
}
}
I merged the stylesheetparser and the stylescombo plugins and want to select mutiple classes from the combo. I made some changes in the onOpen and the onClick functions:
onOpen: function() {
var selection = editor.getSelection(),
selectedElement = selection.getSelectedElement(),
selectedRanges = !selectedElement && selection.getRanges(),
selectedText = !selectedElement && selection.getSelectedText(),
nativeRange,
elementPath = editor.elementPath(),
element,
counter = [ 0, 0, 0, 0 ];
this.showAll();
this.unmarkAll();
for ( var name in styles ) {
var style = styles[ name ],
type = style._.type;
if(type == CKEDITOR.STYLE_OBJECT && (selectedElement || selectedText))
element = !selectedText ? selectedElement : selectedText;
else if((type == CKEDITOR.STYLE_BLOCK || type == CKEDITOR.STYLE_INLINE) && !selectedText && !selectedElement && selectedRanges[ 0 ] && selectedRanges[ 0 ].getCommonAncestor( 1 ))
element = selectedRanges[ 0 ].getCommonAncestor( 1 ).getAscendant( style.element );
else if(type == CKEDITOR.STYLE_INLINE && selectedText && !selectedElement)
element = selectedText;
if(element != null) {
if(style.checkApplicable( elementPath, editor, editor.activeFilter )) {
counter[ type ]++;
var classes = !selectedText ? element.getAttribute( 'class' ) : null,
classArr = classes != null ? classes.split(' ') : [];
if(classArr.length > 0) {
for(var i = 0; i < classArr.length; i++) {
if(classArr[i] == style._.definition.attributes['class'])
this.mark( name );
}
}
}
else
this.hideItem( name );
}
}
if ( !counter[ CKEDITOR.STYLE_BLOCK ] )
this.hideGroup( lang[ 'panelTitle' + String( CKEDITOR.STYLE_BLOCK ) ] );
if ( !counter[ CKEDITOR.STYLE_INLINE ] )
this.hideGroup( lang[ 'panelTitle' + String( CKEDITOR.STYLE_INLINE ) ] );
if ( !counter[ CKEDITOR.STYLE_OBJECT ] )
this.hideGroup( lang[ 'panelTitle' + String( CKEDITOR.STYLE_OBJECT ) ] );
},
onClick: function( value ) {
editor.focus();
editor.fire( 'saveSnapshot' );
var style = styles[ value ],
type = style._.type,
selection = editor.getSelection(),
selectedElement = selection.getSelectedElement(),
selectedRanges = !selectedElement && selection.getRanges(),
selectedText = !selectedElement && selection.getSelectedText(),
element;
if(type == CKEDITOR.STYLE_OBJECT && (selectedElement || selectedText))
element = !selectedText ? selectedElement : selectedText;
else if((type == CKEDITOR.STYLE_BLOCK || type == CKEDITOR.STYLE_INLINE) && !selectedText && !selectedElement && selectedRanges[ 0 ] && selectedRanges[ 0 ].getCommonAncestor( 1 ))
element = selectedRanges[ 0 ].getCommonAncestor( 1 ).getAscendant( style.element );
else if(type == CKEDITOR.STYLE_INLINE && selectedText && !selectedElement)
element = selectedText;
if(element != null) {
var classes = !selectedText ? element.getAttribute( 'class' ) : null,
classArr = classes != null ? classes.split(' ') : [],
selClass = style._.definition.attributes['class'],
styleClasses = CKEDITOR.tools.clone( style );
if(classArr.length > 0 && contains(classArr, selClass) && !selectedText) {
var newClassArr = new Array();
for(var i = 0; i < classArr.length; i++) {
if(classArr[i] != selClass)
newClassArr.push(classArr[i]);
}
if(newClassArr.length == 0) {
editor.removeStyle( styleClasses );
}
else {
styleClasses._.definition.attributes['class'] = newClassArr.join(' ');
editor.applyStyle( styleClasses );
}
}
else {
classArr.push(selClass);
styleClasses._.definition.attributes['class'] = classArr.join(' ');
editor.applyStyle( styleClasses );
}
}
editor.fire( 'saveSnapshot' );
},
It works for block elements and images (didn't checked fake elements), you can select and deselect all classes you want. But I have some trouble with inline elements.
If I click inside a block element, it lists all classes for this element and I can select/deselect what I want/need. If I click inside an inline element, nothing happends. Can getAscendant() not find inline element tags?
If I select text, it lists all classes for inline elements. But I want span classes only and not e.g. strong classes. Classes for the strong tag should only available if I select a strong element. Is it possible? How can I differentiate different inline elements?
Why anchors are objects and not inline elements? If I double click an anchor and close the link dialog, I can select an anchor class.
How to disable the combo, if no classes are available? I can not find the right place.
How to disable the combo close on a class selection? If the selected class is set after close, forget this question - I can live with it :-)
Here is a demo: http://webutler.de/test/index.php?lang=en
The complete file: test/plugins/cssclasscombo/plugin.js
The plugin as zip: test/plugins/cssclasscombo.zip
If you try the plugin, set
config.contentsCss = ['contents.css']; // <= it reads the classes from this file
config.extraPlugins = 'cssclasscombo';
config.removePlugins = 'stylescombo';
config.stylesSet = [];
config.allowedContent = true;
// the plugin works with indent and justify classes =>
config.justifyClasses = [ 'alignleft', 'aligncenter', 'alignright', 'alignjustify' ];
config.indentClasses = [ 'indent1', 'indent2', 'indent3', 'indent4', 'indent5' ];
to your config and use this CSS file: http://www.webutler.de/test/contents.css
Thanx for help
It's not perfect, but the plugin works now. If you can need it, here is the download:
http://webutler.de/download/ckeditor_plugins/cssclasscombo.zip
Settings in your config.js:
config.removePlugins = 'stylescombo';
config.extraPlugins = 'cssclasscombo';
config.stylesSet = [];
config.contentsCss = ['contents.css']; // <= classes are read from this file(s)
I have a array of thread objects, each thread object with the properties
unit:number
task:number
subtask:number
I want to create a pipe to filter after these threads, so far I have a working pipe like below. I'm not really satisfied with it yet and wanted to ask you guys if there is a more elegant solution?
HTML:
<div class="thread-item" *ngFor="#thread of threadlist | threadPipe:unitPipe:taskPipe:subtaskPipe"></div>
Pipe.ts
export class ThreadPipe implements PipeTransform{
threadlistCopy:Thread[]=[];
transform(array:Thread[], [unit,task,subtask]):any{
//See all Threads
if(unit == 0 && task == 0 && subtask == 0){
return array
}
//See selected Units only
if(unit != 0 && task == 0 && subtask == 0){
this.threadlistCopy=[];
for (var i = 0; i<array.length;i++){
if(array[i].unit == unit){
this.threadlistCopy.push(array[i])
}
}
return this.threadlistCopy
}
//See selected Units and Tasks
if (unit != 0 && task != 0 && subtask == 0){
this.threadlistCopy=[];
for (var i = 0; i<array.length;i++){
if(array[i].unit == unit && array[i].task == task){
this.threadlistCopy.push(array[i])
}
}
return this.threadlistCopy
}
// See selected units, tasks, subtask
if (unit != 0 && task != 0 && subtask != 0){
this.threadlistCopy=[];
for (var i = 0; i<array.length;i++){
if(array[i].unit == unit && array[i].task == task && array[i].subtask == subtask){
this.threadlistCopy.push(array[i])
}
}
return this.threadlistCopy
}
}
}
You are implementing your pipe the right way, but you are basically re-inventing the Array.prototype.filter mechanism in your code. A simpler way will be:
export class ThreadPipe implements PipeTransform{
transform(array:Thread[], [unit,task,subtask]):any{
//See all Threads
if(unit == 0 && task == 0 && subtask == 0){
return array
}
//See selected Units only
if(unit != 0 && task == 0 && subtask == 0){
return array.filter(thread => {
return thread.unit === unit;
});
}
//See selected Units and Tasks
if (unit != 0 && task != 0 && subtask == 0){
return array.filter(thread => {
return thread.unit === unit && thread.task === task;
});
}
// See selected units, tasks, subtask
if (unit != 0 && task != 0 && subtask != 0){
return array.filter(thread => {
return thread.unit === unit && thread.task === task && thread.subtask === subtask;
});
}
}
}
I was recently asked in the interview
What would be the efficient algorithm to find if two given binary trees are structurally identical but not the content?
a
/ \
b c
\
e
z
/ \
u v
\
t
are structurally identical.
Next question was find out if 2 binary tree are structurally mirror ?
Any pointer or help are appreciated.
My attempt was
boolean isStrucutrallyIdentitical(BinaryNode root1, BinayNode root2) {
if(root1==null && root2==null) return true;
if(root1==null || root2==null) return false;
if(root1!=null && roo2!=null) return true; // instead of value just check if its null or not
return isStrucutrallyIdentitical(root1.getLeft(), root2.getLeft()) && isStrucutrallyIdentitical(root1.getRight(), root2.getRight());
}
public boolean areStructuralySame(TreeNode<Integer> tree1, TreeNode<Integer> tree2) {
if(tree1 == null && tree2 == null) {
return true;
}
if(tree1 == null || tree2 == null) {
return false;
} else return (areStructuralySame(tree1.getLeft(), tree2.getLeft()) && areStructuralySame(tree1.getRight(), tree2.getRight()));
}
This works fine
private static boolean compare(TNode curRoot, TNode newRoot) {
if (curRoot == null && newRoot == null) {
return true;
} else if ((curRoot == null && newRoot != null) || (curRoot != null && newRoot == null))
return false;
else {
if (compare(curRoot.left, newRoot.left) && compare(curRoot.right, newRoot.right))
return true;
return false;
}
}
I have this condition
if(Model.Bids != null && Model.Bids.Items != null && Model.Bids.Items.Count > 0)
{
...
}
Problem is, I think this is ugly. I could write a function that encapsulates this but I wonder if there is something else that would help me write something like just the important bits below without having to do the null checks. If not then this would be a handy language extension.
if(Model.Bids.Items.Count > 0)
{
...
}
For c# this two options achieve sort of what you want but I wouldn't put this in my software quickly.
Also I doubt this gets more readable or better understandable. There is one other option but that requires you to refactor you Model class chain. If you implement a NullObject for the type in Bids and the type in Item you can do if(Model.Bids.Items.Count > 0) because all types will not be null but have an implementation that handles the Empty state (much like String.Empty)
helpers
/* take a func, wrap in a try/catch, invoke compare */
bool tc(Func<bool> comp )
{
try
{
return comp.Invoke();
}
catch (Exception)
{
return false;
}
}
/* helper for f */
T1 f1<T,T1>(T root, Func<T, T1> p1) where T:class
{
T1 res = default(T1);
if (root != null)
{
res = p1.Invoke(root);
}
return res;
}
/* take a chain of funcs and a comp if the last
in the chain is still not null call comp (expand if needed) */
bool f<T,T1,T2,TL>( T root, Func<T,T1> p1, Func<T1,T2> p2, Func<T2,TL> plast,
Func<TL, bool> comp) where T:class where T1:class where T2:class
{
var allbutLast = f1(f1(root, p1), p2);
return allbutLast != null && comp(plast.Invoke(allbutLast));
}
Usage
var m = new Model();
if (f(m, p => p.Bids, p => p.Items, p => p.Count, p => p > 0))
{
Debug.WriteLine("f");
}
if (tc(() => m.Bids.Items.Count > 0))
{
Debug.WriteLine("tc ");
}
if (m.Bids != null && m.Bids.Items != null && m.Bids.Items.Count > 0)
{
Debug.WriteLine("plain");
}