Skip to content

Commit 27bac93

Browse files
authored
Implement iterators on Record (#530) (#530)
This PR adds various iterables to the Record class: values(), entries(), [Symbol.iterator](). The new functionality allows, among other things, getting values lazily, on-demand, and / or in pairs: const queryResult = await session.run(query); for (const record of queryResult.records) for (const [ key, value ] of record.entries()) // do something with 'key' and 'value' for (const record of queryResult.records) for (const value of record) // each queried value is accessible as 'value'
1 parent 743f82a commit 27bac93

File tree

4 files changed

+120
-6
lines changed

4 files changed

+120
-6
lines changed

src/record.js

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ class Record {
8080
* @param {function(value: Object, key: string, record: Record)} visitor the function to apply to each field.
8181
*/
8282
forEach (visitor) {
83-
for (let i = 0; i < this.keys.length; i++) {
84-
visitor(this._fields[i], this.keys[i], this)
83+
for (const [key, value] of this.entries()) {
84+
visitor(value, key, this)
8585
}
8686
}
8787

@@ -98,23 +98,59 @@ class Record {
9898
map (visitor) {
9999
const resultArray = []
100100

101-
for (let i = 0; i < this.keys.length; i++) {
102-
resultArray.push(visitor(this._fields[i], this.keys[i], this))
101+
for (const [key, value] of this.entries()) {
102+
resultArray.push(visitor(value, key, this))
103103
}
104104

105105
return resultArray
106106
}
107107

108+
/**
109+
* Iterate over results. Each iteration will yield an array
110+
* of exactly two items - the key, and the value (in order).
111+
*
112+
* @generator
113+
* @returns {IterableIterator<[string, Object]>}
114+
*/
115+
* entries () {
116+
for (let i = 0; i < this.keys.length; i++) {
117+
yield [this.keys[i], this._fields[i]]
118+
}
119+
}
120+
121+
/**
122+
* Iterate over values.
123+
*
124+
* @generator
125+
* @returns {IterableIterator<Object>}
126+
*/
127+
* values () {
128+
for (let i = 0; i < this.keys.length; i++) {
129+
yield this._fields[i]
130+
}
131+
}
132+
133+
/**
134+
* Iterate over values. Delegates to {@link Record#values}
135+
*
136+
* @generator
137+
* @returns {IterableIterator<Object>}
138+
*/
139+
* [Symbol.iterator] () {
140+
yield * this.values()
141+
}
142+
108143
/**
109144
* Generates an object out of the current Record
110145
*
111146
* @returns {Object}
112147
*/
113148
toObject () {
114149
const object = {}
115-
this.forEach((value, key) => {
150+
151+
for (const [key, value] of this.entries()) {
116152
object[key] = value
117-
})
153+
}
118154

119155
return object
120156
}

test/record.test.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,67 @@ describe('#unit Record', () => {
126126
// Then
127127
expect(result).toEqual([['Bob', 'name', record], [45, 'age', record]])
128128
})
129+
130+
it('should allow taking values lazily', () => {
131+
// Given
132+
const record = new Record(['name', 'age'], ['Bob', 45])
133+
const values = record.values()
134+
135+
// When
136+
const first = values.next()
137+
const second = values.next()
138+
const third = values.next()
139+
140+
// Then
141+
expect(first.value).toEqual('Bob')
142+
expect(first.done).toBeFalsy()
143+
expect(second.value).toEqual(45)
144+
expect(second.done).toBeFalsy()
145+
expect(third.value).toBeUndefined()
146+
expect(third.done).toBeTruthy()
147+
})
148+
149+
it('should allow taking key-value pairs lazily', () => {
150+
// Given
151+
const record = new Record(['name', 'age'], ['Bob', 45])
152+
const entries = record.entries()
153+
154+
// When
155+
const first = entries.next()
156+
const second = entries.next()
157+
const third = entries.next()
158+
159+
// Then
160+
expect(first.value).toEqual(['name', 'Bob'])
161+
expect(first.done).toBeFalsy()
162+
expect(second.value).toEqual(['age', 45])
163+
expect(second.done).toBeFalsy()
164+
expect(third.value).toBeUndefined()
165+
expect(third.done).toBeTruthy()
166+
})
167+
168+
it('should allow directly creating array from record', () => {
169+
// Given
170+
const record = new Record(['name', 'age'], ['Bob', 45])
171+
172+
// When
173+
const values = Array.from(record)
174+
175+
// Then
176+
expect(values).toEqual(['Bob', 45])
177+
})
178+
179+
it('should allow iterating over values using for..of loop', () => {
180+
// Given
181+
const record = new Record(['name', 'age'], ['Bob', 45])
182+
const values = []
183+
184+
// When
185+
for (const value of record) {
186+
values.push(value)
187+
}
188+
189+
// Then
190+
expect(values).toEqual(['Bob', 45])
191+
})
129192
})

test/types/record.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ record1.forEach((value: any, key: string) => {})
3737

3838
record1.forEach((value: any, key: string, record: Record) => {})
3939

40+
const record1Entries: IterableIterator<[string, any]> = record1.entries()
41+
const record2Entries: IterableIterator<[string, any]> = record2.entries()
42+
43+
const record1Values: IterableIterator<any> = record1.values()
44+
const record2Values: IterableIterator<any> = record2.values()
45+
46+
const record1ToArray: any[] = [...record1]
47+
const record2ToArray: any[] = [...record2]
48+
4049
const record1Has: boolean = record1.has(42)
4150
const record2Has: boolean = record1.has('key')
4251

types/record.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ declare class Record {
3535

3636
map<T>(visitor: MapVisitor<T>): T[]
3737

38+
entries(): IterableIterator<[string, Object]>
39+
40+
values(): IterableIterator<Object>
41+
42+
[Symbol.iterator](): IterableIterator<Object>
43+
3844
toObject(): object
3945

4046
get(key: string | number): any

0 commit comments

Comments
 (0)