desc: Tests manipulation operations on arrays
tests:

    # Define a sequence to work with
    - def: arr = r.expr([1, 2, 3])
    - def: dupe_arr = r.expr([1, 1, 2, 3])
    - def: objArr = r.expr([{'a':1, 'b':'a'}, {'a':2, 'b':'b'}, {'a':3, 'b':'c'}])
    - def: nestedObjArr = r.expr([{'a':1, 'b':{'c':1}}, {'a':2, 'b':{'c':2}}, {'a':3, 'b':{'c':3}}])

    ## Append

    - cd: arr.append(4)
      ot: [1,2,3,4]
    - cd: arr.append('a')
      ot: [1,2,3,'a']

    ## Prepend

    - cd: arr.prepend(0)
      ot: [0,1,2,3]
    - cd: arr.prepend('a')
      ot: ['a',1,2,3]

    ## Difference

    - cd: arr.difference([1,2,2])
      ot: [3]
    - cd: arr.difference([])
      ot: [1,2,3]
    - cd: arr.difference(["foo", "bar"])
      ot: [1,2,3]

    ## Set operations
    - cd: dupe_arr.set_insert(1)
      ot: [1,2,3]
    - cd: dupe_arr.set_insert(4)
      ot: [1,2,3,4]

    - cd: dupe_arr.set_union([3,4,5,5])
      ot: [1,2,3,4,5]
    - cd: dupe_arr.set_union([5,6])
      ot: [1,2,3,5,6]

    - cd: dupe_arr.set_intersection([1,1,1,2,2])
      ot: [1,2]
    - cd: dupe_arr.set_intersection(["foo"])
      ot: []

    - cd: dupe_arr.set_difference([1,1,1,10])
      ot: [2,3]
    - cd: dupe_arr.set_difference([2])
      ot: [1,3]

    ## Slice

    # Python uses the slice syntax
    - py: ["arr[1:3]", "arr.slice(1, 3)"]
      js: arr.slice(1,3)
      rb: [ "arr[(1..2)]", "arr[(1...3)]", "arr.slice(1, 2, :right_bound => 'closed')"]
      ot: [2, 3]

    # One ended slices
    - py: arr[:2]
      js: arr.slice(0,2)
      rb: [ "arr[(0..1)]", "arr[(0...2)]" ]
      ot: [1,2]
    - py: arr[1:]
      js:
        cd: arr.slice(1)
        ot: err("RqlDriverError", "Expected 2 argument(s) but found 1.")
      rb: []
      ot: [2,3]

    # Negative indicies
    # Python 2.x doesn't handle negative indicies properly
    - cd: arr.slice(-2, -1)
      rb: arr[(-2...-1)]
      ot: [2]

    ## Skip

    - cd: arr.skip(1)
      ot: [2,3]
    - cd: arr.skip(2)
      ot: [3]
    - cd: arr.skip(12)
      ot: []

    ## Limit

    - cd: arr.limit(2)
      ot: [1,2]
    - cd: arr.limit(0)
      ot: []
    - cd: arr.limit(12)
      ot: [1,2,3]

    ## Pluck

    - cd: objArr.pluck('a', 'b')
      ot: [{'a':1, 'b':'a'}, {'a':2, 'b':'b'}, {'a':3, 'b':'c'}]
    - cd: objArr.pluck('a')
      ot: [{'a':1}, {'a':2}, {'a':3}]
    - cd: objArr.pluck()
      ot: [{}, {}, {}]

    ## With_Fields
    - def: wftst = objArr.union(objArr.pluck('a')).union(objArr.pluck('b')).union([{'a':null}])
    - cd: wftst.with_fields('a')
      ot: ([{'a':1},{'a':2},{'a':3},{'a':1},{'a':2},{'a':3}])
    - cd: wftst.with_fields('b')
      ot: ([{'b':'a'},{'b':'b'},{'b':'c'},{'b':'a'},{'b':'b'},{'b':'c'}])
    - cd: wftst.with_fields('a', 'b')
      ot: ([{'a':1,'b':'a'},{'a':2,'b':'b'},{'a':3,'b':'c'}])
    - cd: wftst.with_fields()
      ot: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}]

    - def: wftst2 = nestedObjArr.union(objArr.pluck({'b':'missing'})).union(nestedObjArr.pluck({'b':'c'}))
    - cd: wftst2.with_fields({'b':'c'})
      ot: ([{'b':{'c':1}}, {'b':{'c':2}}, {'b':{'c':3}}, {'b':{'c':1}}, {'b':{'c':2}}, {'b':{'c':3}}])

    - cd: wftst.with_fields(1)
      ot: err("RqlRuntimeError", "Invalid path argument `1`.", [])
    - cd: r.expr(1).with_fields()
      ot: err("RqlRuntimeError", "Cannot perform has_fields on a non-object non-sequence `1`.", [])

    ## Without

    - cd: objArr.without('a', 'b')
      ot: [{}, {}, {}]
    - cd: objArr.without('a')
      ot: [{'b':'a'}, {'b':'b'}, {'b':'c'}]
    - cd: objArr.without()
      ot: [{'a':1, 'b':'a'}, {'a':2, 'b':'b'}, {'a':3, 'b':'c'}]

    ## Map

    - py: "arr.map(lambda v: v + 1)"
      js: arr.map(function(v) { return v.add(1); })
      rb: arr.map{ |v| v + 1 }
      ot: [2,3,4]
    #- py: objArr.map(r.row['b'])
    #  js: objArr.map(r.row('b'))
    #  ot: ['a', 'b', 'c']

    ## Reduce

    - py: "arr.reduce(lambda a, b: a + b)"
      js: arr.reduce(function(a,b) { return a.add(b); })
      rb: arr.reduce{ |a, b| a + b }
      ot: 6

    - py: arr.reduce(lambda a, b:a + b, base=0)
      js: arr.reduce(function(a,b) { return a.add(b); }, 0)
      rb: arr.reduce(0){ |a, b| a + b }
      ot: 6

    - py: "arr.union(arr).reduce(lambda a, b: a + b)"
      js: arr.union(arr).reduce(function(a,b) { return a.add(b); })
      rb: arr.union(arr).reduce{ |a, b| a + b }
      ot: 12

    - py: arr.union(arr).reduce(lambda a, b:a + b, base=0)
      js: arr.union(arr).reduce(function(a,b) { return a.add(b); }, 0)
      rb: arr.union(arr).reduce(0){ |a, b| a + b }
      ot: 12

    ## Filter

    - py: "objArr.filter(lambda row: row['b'] == 'b')"
      js: objArr.filter(function(row) { return row('b').eq('b'); })
      rb: objArr.filter{ |row| row[:b].eq 'b' }
      ot: [{'a':2, 'b':'b'}]

    ## ConcatMap

    - py: "arr.concat_map(lambda v: [1,2])"
      js: "arr.concatMap(function(v) { return [1,2]; })"
      rb: arr.concat_map{ |v| [1,2] }
      ot: [1,2,1,2,1,2]
    - py: "arr.concat_map(lambda v: [{'v':v}, {'v2':v + 1}])"
      js: "arr.concatMap(function(v) { return [{'v':v}, {'v2':v.add(1)}]; })"
      rb: arr.concat_map{ |v| [{:v => v}, {:v2 => v + 1}] }
      ot: [{'v':1}, {'v2':2}, {'v':2}, {'v2':3}, {'v':3}, {'v2':4}]

    ## OrderBy

    - cd: objArr.order_by('b')
      rb: objArr.order_by :b
      ot: [{'a':1, 'b':'a'}, {'a':2, 'b':'b'}, {'a':3, 'b':'c'}]

    - cd: objArr.order_by(r.desc('b'))
      ot: [{'a':3, 'b':'c'}, {'a':2, 'b':'b'}, {'a':1, 'b':'a'}]

    - cd: r.expr([{'-a':1},{'-a':2}]).order_by('-a')
      rb: r.expr([{ '-a' => 1}, {'-a' => 2}]).order_by('-a')
      ot:
        cd: [{'-a':1},{'-a':2}]
        rb: "[{'-a'=>1},{'-a'=>2}]"

    ## Distinct

    - cd: r.expr([1,1,2,2,2,3,4]).distinct()
      ot: [1,2,3,4]

    ## Count

    - cd: arr.count()
      ot: 3
      cd: objArr.count()
      ot: 3

    ## Union

    - cd: arr.union(objArr)
      ot: [1, 2, 3, {'a':1, 'b':'a'}, {'a':2, 'b':'b'}, {'a':3, 'b':'c'}]

    ## Nth

    - cd: ["arr[1]", "arr.nth(1)"]
      js: arr.nth(1)
      ot: 2
    - py: arr[0]
      rb: arr[0]
      js: arr.nth(0)
      ot: 1

    ## Is Empty

    - cd: r.expr([]).is_empty()
      ot: true
    - cd: arr.is_empty()
      ot: false

    ## Contains
    - cd: arr.contains(2)
      ot: true
    - cd: arr.contains(2, 3)
      ot: true
    - cd: arr.contains(4)
      ot: false
    - cd: arr.contains(2, 4)
      ot: false
    - cd: arr.contains(2, 2)
      ot: false
    - cd: arr.union(arr).contains(2, 2)
      ot: true

    - cd: arr.contains{|x| x.eq(2)}
      py: arr.contains(lambda x:x == 2)
      js: arr.contains(function(x){return x.eq(2);})
      ot: true
    - cd: arr.contains(lambda {|x| x.eq(2)}, lambda {|x| x.eq(3)})
      py: arr.contains(lambda x:x == 2, lambda x:x==3)
      js: arr.contains(function(x){return x.eq(2);}, function(x){return x.eq(3);})
      ot: true
    - cd: arr.contains{|x| x.eq(4)}
      py: arr.contains(lambda x:x == 4)
      js: arr.contains(function(x){return x.eq(4);})
      ot: false
    - cd: arr.contains(lambda {|x| x.eq(2)}, lambda {|x| x.eq(4)})
      py: arr.contains(lambda x:x == 2, lambda x:x==4)
      js: arr.contains(function(x){return x.eq(2);}, function(x){return x.eq(4);})
      ot: false
    - cd: arr.contains(lambda {|x| x.eq(2)}, lambda {|x| x.eq(2)})
      py: arr.contains(lambda x:x == 2, lambda x:x==2)
      js: arr.contains(function(x){return x.eq(2);}, function(x){return x.eq(2);})
      ot: false
    - cd: arr.union(arr).contains(lambda {|x| x.eq(2)}, lambda {|x| x.eq(2)})
      py: arr.union(arr).contains(lambda x:x == 2, lambda x:x==2)
      js: arr.union(arr).contains(function(x){return x.eq(2);}, function(x){return x.eq(2);})
      ot: true

    ## Get Field
    - cd: r.expr([{'a':1},{'b':2},{'a':3,'c':4}])['a']
      js: r.expr([{'a':1},{'b':2},{'a':3,'c':4}])('a')
      ot: [1, 3]
    - cd: r.expr([{'a':1},'a',{'b':2},{'a':3,'c':4}])['a']
      js: r.expr([{'a':1},'a',{'b':2},{'a':3,'c':4}])('a')
      ot: err("RqlRuntimeError", "Cannot perform get_field on a non-object non-sequence `\"a\"`.", [])

    ## Grouped Map Reduce

    ## Group by

    ## Inner Join

    ## Outer Join
