Этот вопрос, кажется, очень популярен здесь в stackoverflow, поэтому я решил попробовать и дать лучший ответ, чтобы помочь людям, начинающим в мире iOS, как я.
Надеюсь, что этот ответ ясен
Передача данных вперед
Передача данных в контроллер просмотра с другого контроллера. Вы должны использовать этот метод, если хотите передать объект / значение с одного контроллера вида на другой контроллер представления, который вы можете нажать в стек навигации.
В этом примере мы будем иметь ViewControllerA
и ViewControllerB
Чтобы передать значение BOOL
с ViewControllerA
на ViewControllerB
, мы сделали бы следующее.
ViewControllerB.h
создать свойство для BOOL
@property (nonatomic, assign) BOOL isSomethingEnabled;
ViewControllerA
вам нужно рассказать об этом ViewControllerB
, поэтому используйте #import "ViewControllerB.h"
Затем, где вы хотите загрузить представление, например. didSelectRowAtIndex
или несколько IBAction
вам нужно установить свойство в ViewControllerB
, прежде чем вы нажмете его на стек навигатора. ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.isSomethingEnabled = YES;
[self pushViewController:viewControllerB animated:YES];
Это установит isSomethingEnabled
в значение ViewControllerB
на BOOL
значение YES
. Передача данных вперед с помощью Segues
Если вы используете раскадровки вы, скорее всего, используете segues и будете нуждаться в этой процедуре для передачи данных вперед. Это похоже на приведенное выше, но вместо передачи данных перед тем, как вы нажимаете контроллер вида, вы используете метод, называемый
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
. Итак, чтобы передать BOOL
с ViewControllerA
на ViewControllerB
, мы выполнил бы следующее:
ViewControllerB.h
создать свойство для BOOL
@property (nonatomic, assign) BOOL isSomethingEnabled;
ViewControllerA
, вам нужно рассказать об этом ViewControllerB
поэтому используйте #import "ViewControllerB.h"
ViewControllerA
до ViewControllerB
на раскадровке и дайте ему идентификатор, в этом примере мы будем называть его "showDetailSegue"
ViewControllerA
, который вызывается, когда выполняется какой-либо segue, из-за этого нам нужно определить, какой вызов был вызван, а затем что-то сделать. В нашем примере мы проверим "showDetailSegue"
, и если это будет выполнено, мы передадим наше значение BOOL
в ViewControllerB
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"showDetailSegue"]){
ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
controller.isSomethingEnabled = YES;
}
}
. Если у вас есть свои представления, встроенные в контроллер навигации, вам нужно немного изменить метод выше на следующие -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"showDetailSegue"]){
UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
ViewControllerB *controller = (ViewControllerB *)navController.topViewController;
controller.isSomethingEnabled = YES;
}
}
Это установит isSomethingEnabled
в ViewControllerB
на значение BOOL
YES
. Передача данных Назад
Чтобы передать данные с ViewControllerB
] до ViewControllerA
вам нужно использовать Protocols and Delegates или Blocks , последний может использоваться как слабо связанный механизм для обратных вызовов.
To сделаем это, мы сделаем ViewControllerA
делегатом из ViewControllerB
. Это позволяет ViewControllerB
отправить сообщение обратно на ViewControllerA
, чтобы мы могли отправить данные назад.
Чтобы ViewControllerA
был делегатом ViewControllerB
, он должен соответствовать протоколу ViewControllerB
, который мы должны указать. Это указывает ViewControllerA
, какие методы он должен реализовать.
ViewControllerB.h
ниже #import
, но выше @interface
вы указываете протокол. @class ViewControllerB;
@protocol ViewControllerBDelegate
- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
@end
ViewControllerB.h
вам нужно настроить свойство delegate
и синтезировать в ViewControllerB.m
@property (nonatomic, weak) id delegate;
ViewControllerB
мы вызываем сообщение на delegate
, когда мы выходим на контроллер вида. NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
ViewControllerB
. Теперь в ViewControllerA.h
скажите ViewControllerA
, чтобы импортировать ViewControllerB
и соответствовать его протоколу. #import "ViewControllerB.h"
@interface ViewControllerA : UIViewController
ViewControllerA.m
реализуем следующий метод из нашего протокола - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
{
NSLog(@"This was returned from ViewControllerB %@",item);
}
viewControllerB
в стек навигации нам нужно сообщить ViewControllerB
, что ViewControllerA
является его делегатом, иначе мы получим сообщение об ошибке. ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.delegate = self
[[self navigationController] pushViewController:viewControllerB animated:YES];
Прежде чем продолжить: Эта операция еще одна groupByKey
.
Не совсем сжатое или эффективное решение, но вы можете использовать UserDefinedAggregateFunction
, введенный в Spark 1.5.0:
object GroupConcat extends UserDefinedAggregateFunction {
def inputSchema = new StructType().add("x", StringType)
def bufferSchema = new StructType().add("buff", ArrayType(StringType))
def dataType = StringType
def deterministic = true
def initialize(buffer: MutableAggregationBuffer) = {
buffer.update(0, ArrayBuffer.empty[String])
}
def update(buffer: MutableAggregationBuffer, input: Row) = {
if (!input.isNullAt(0))
buffer.update(0, buffer.getSeq[String](0) :+ input.getString(0))
}
def merge(buffer1: MutableAggregationBuffer, buffer2: Row) = {
buffer1.update(0, buffer1.getSeq[String](0) ++ buffer2.getSeq[String](0))
}
def evaluate(buffer: Row) = UTF8String.fromString(
buffer.getSeq[String](0).mkString(","))
}
Пример использования:
val df = sc.parallelize(Seq(
("username1", "friend1"),
("username1", "friend2"),
("username2", "friend1"),
("username2", "friend3")
)).toDF("username", "friend")
df.groupBy($"username").agg(GroupConcat($"friend")).show
## +---------+---------------+
## | username| friends|
## +---------+---------------+
## |username1|friend1,friend2|
## |username2|friend1,friend3|
## +---------+---------------+
Вы также можете создать оболочку Python, как показано в Spark: как сопоставить Python с Scala или Java User Defined Functions?
На практике может быть быстрее извлечь RDD, groupByKey
, mkString
и перестроить DataFrame.
Вы можете получить аналогичный эффект, объединив collect_list
(Spark> = 1.6.0) с concat_ws
:
import org.apache.spark.sql.functions.{collect_list, udf, lit}
df.groupBy($"username")
.agg(concat_ws(",", collect_list($"friend")).alias("friends"))
Язык: Scala Spark version: 1.5.2
У меня была такая же проблема, а также попытался разрешить ее с помощью udfs
, но, к сожалению, это привело к появлению большего количества проблем позже в коде из-за тип несоответствий. Я смог обойти это, сначала преобразовывая DF
в RDD
, а затем группируя и обрабатывая данные желаемым способом, а затем преобразовывая RDD
обратно в DF
следующим образом:
val df = sc
.parallelize(Seq(
("username1", "friend1"),
("username1", "friend2"),
("username2", "friend1"),
("username2", "friend3")))
.toDF("username", "friend")
+---------+-------+
| username| friend|
+---------+-------+
|username1|friend1|
|username1|friend2|
|username2|friend1|
|username2|friend3|
+---------+-------+
val dfGRPD = df.map(Row => (Row(0), Row(1)))
.groupByKey()
.map{ case(username:String, groupOfFriends:Iterable[String]) => (username, groupOfFriends.mkString(","))}
.toDF("username", "groupOfFriends")
+---------+---------------+
| username| groupOfFriends|
+---------+---------------+
|username1|friend2,friend1|
|username2|friend3,friend1|
+---------+---------------+
Вы можете попробовать функцию collect_list
sqlContext.sql("select A, collect_list(B), collect_list(C) from Table1 group by A
Или вы можете зарегистрировать UDF что-то вроде
sqlContext.udf.register("myzip",(a:Long,b:Long)=>(a+","+b))
, и вы можете использовать эту функцию в запросе
sqlConttext.sql("select A,collect_list(myzip(B,C)) from tbl group by A")
Один способ сделать это с помощью pyspark & lt; 1.6, который, к сожалению, не поддерживает определяемую пользователем агрегированную функцию:
byUsername = df.rdd.reduceByKey(lambda x, y: x + ", " + y)
, и если вы хотите снова сделать это:
sqlContext.createDataFrame(byUsername, ["username", "friends"])
Начиная с версии 1.6, вы может использовать collect_list , а затем присоединиться к созданному списку:
from pyspark.sql import functions as F
from pyspark.sql.types import StringType
join_ = F.udf(lambda x: ", ".join(x), StringType())
df.groupBy("username").agg(join_(F.collect_list("friend").alias("friends"))
Вот функция, которую вы можете использовать в PySpark:
import pyspark.sql.functions as F
def group_concat(col, distinct=False, sep=','):
if distinct:
collect = F.collect_set(col.cast(StringType()))
else:
collect = F.collect_list(col.cast(StringType()))
return F.concat_ws(sep, collect)
table.groupby('username').agg(F.group_concat('friends').alias('friends'))
В SQL:
select username, concat_ws(',', collect_list(friends)) as friends
from table
group by username