diff --git a/ibis/expr/operations/generic.py b/ibis/expr/operations/generic.py index a28fe11d3e23..2fede0182005 100644 --- a/ibis/expr/operations/generic.py +++ b/ibis/expr/operations/generic.py @@ -373,6 +373,23 @@ def has_resolved_name(self): return True +@public +class StructColumn(Value): + names = rlz.tuple_of(rlz.instance_of(str), min_length=1) + values = rlz.tuple_of(rlz.any, min_length=1) + + output_shape = rlz.Shape.COLUMNAR + + @immutable_property + def output_dtype(self): + return dt.Struct.from_tuples( + zip(self.names, (value.type() for value in self.values)) + ) + + def root_tables(self): + return distinct_roots(*self.values) + + @public class DecimalPrecision(Unary): arg = rlz.decimal diff --git a/ibis/expr/types/structs.py b/ibis/expr/types/structs.py index 11df9478d0ff..ff1c8d72b108 100644 --- a/ibis/expr/types/structs.py +++ b/ibis/expr/types/structs.py @@ -46,7 +46,15 @@ def struct( Create a struct literal from a [`dict`][dict] with a specified type >>> t = ibis.struct(dict(a=1, b='foo'), type='struct') """ - return literal(collections.OrderedDict(value), type=type) + import ibis.expr.operations as ops + + items = dict(value) + values = items.values() + if any(isinstance(value, Value) for value in values): + return ops.StructColumn( + names=tuple(items.keys()), values=tuple(values) + ).to_expr() + return literal(collections.OrderedDict(items), type=type) @public