• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

WillemOpperman / csv-diff-dotnet / 6839269571

12 Nov 2023 07:12AM UTC coverage: 76.233% (+0.3%) from 75.909%
6839269571

push

github

web-flow
Merge pull request #51 from WillemOpperman/WillemOpperman-patch-1

feat: allow targeting `net48`

340 of 446 relevant lines covered (76.23%)

1115.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.63
csv-diff/Algorithm.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using csv_diff.Interfaces;
5

6
namespace csv_diff
7
{
8
    // Implements the CSV diff algorithm.
9
    public class Algorithm
10
    {
11
        // Diffs two CSVSource structures.
12
        public Dictionary<string, Diff> DiffSources(
13
            ISource left,
14
            ISource right,
15
            string[] keyFields,
16
            string[] diffFields,
17
            IDictionary<string, object> options = null)
18
        {
19
            if (left.CaseSensitive != right.CaseSensitive)
5✔
20
            {
21
                throw new ArgumentException("Left and right must have the same settings for case-sensitivity");
×
22
            }
23

24
            if (left.ParentFields.Count != right.ParentFields.Count)
5✔
25
            {
26
                throw new ArgumentException("Left and right must have the same settings for parent/child fields");
×
27
            }
28

29
            // Ensure key fields are not also in the diff_fields
30
            var diffFieldSet = new HashSet<string>(diffFields);
5✔
31
            var diffFieldsNoKeys = diffFieldSet.Except(keyFields).ToArray();
5✔
32

33
            var leftIndex = left.Index;
5✔
34
            var leftValues = left.Lines;
5✔
35
            var leftKeys = leftValues.Keys;
5✔
36
            var rightIndex = right.Index;
5✔
37
            var rightValues = right.Lines;
5✔
38
            var rightKeys = rightValues.Keys;
5✔
39
            var parentFieldCount = left.ParentFields.Count;
5✔
40

41
            var includeAdds = options.TryGetValue("ignore_adds", out var ignoreAdds) ? !(bool)ignoreAdds : true;
5✔
42
            var includeMoves = options.TryGetValue("ignore_moves", out var ignoreMoves) ? !(bool)ignoreMoves : true;
5✔
43
            var includeUpdates = options.TryGetValue("ignore_updates", out var ignoreUpdated) ? !(bool)ignoreUpdated : true;
5✔
44
            var includeDeletes = options.TryGetValue("ignore_deletes", out var ignoreDeletes) ? !(bool)ignoreDeletes : true;
5✔
45

46
            var caseSensitive = left.CaseSensitive;
5✔
47
            var equalityProcs = options?.ContainsKey("equality_procs") == true
5✔
48
                ? (IDictionary<string, Func<object, object, bool>>)options["equality_procs"]
5✔
49
                : new Dictionary<string, Func<object, object, bool>>();
5✔
50

51
            var diffs = new Dictionary<string, Diff>();
5✔
52
            var potentialMoves = new Dictionary<string, List<string>>();
5✔
53

54
            // First identify deletions
55
            if (includeDeletes)
5✔
56
            {
57
                foreach (var key in leftKeys.Except(rightKeys))
32✔
58
                {
59
                    // Delete
60
                    var keyVals = key.Split('~');
11✔
61
                    var parent = string.Join("~", keyVals.Take(parentFieldCount));
11✔
62
                    var child = string.Join("~", keyVals.Skip(parentFieldCount));
11✔
63
                    var leftParent = leftIndex[parent];
11✔
64
                    var leftValue = leftValues[key];
11✔
65
                    
66
                    var rowIdx = leftKeys.IndexOf(key);
11✔
67
                    var sibIdx = leftParent.IndexOf(key);
11✔
68
                    if (sibIdx < 0)
11✔
69
                    {
70
                        throw new Exception($"Can't locate key {key} in parent {parent}");
×
71
                    }
72

73
                    diffs[key] = new Diff("delete", leftValue, rowIdx, sibIdx);
11✔
74
                    if (!potentialMoves.ContainsKey(child))
11✔
75
                    {
76
                        potentialMoves[child] = new List<string>();
11✔
77
                    }
78

79
                    potentialMoves[child].Add(key);
11✔
80
                }
81
            }
82

83
            // Now identify adds/updates
84
            foreach (var key in rightKeys)
2,056✔
85
            {
86
                var keyVals = key.Split('~');
1,023✔
87
                var parent = string.Join("~", keyVals.Take(parentFieldCount));
1,023✔
88
                var leftParent = leftIndex.ContainsKey(parent) ? leftIndex[parent] : null;
1,023✔
89
                var rightParent = rightIndex[parent];
1,023✔
90
                var leftValue = leftValues.ContainsKey(key) ? leftValues[key] : null;
1,023✔
91
                var rightValue = rightValues[key];
1,023✔
92
                var leftIdx = leftParent?.IndexOf(key) ?? -1;
1,023✔
93
                var rightIdx = rightParent.IndexOf(key);
1,023✔
94

95
                if (leftIdx >= 0 && rightIdx >= 0)
1,023✔
96
                {
97
                    if (includeUpdates && diffFieldsNoKeys.Length > 0)
1,008✔
98
                    {
99
                        var changes = DiffRow(leftValue, rightValue, diffFieldsNoKeys, caseSensitive, equalityProcs);
1,008✔
100
                        if (changes.Count > 0)
1,008✔
101
                        {
102
                            var id = IdFields(keyFields, rightValue);
4✔
103
                            
104
                            diffs[key] = new Diff("update", id.Union(changes).ToDictionary(x => x.Key, x => x.Value), rightIdx, rightIdx);
28✔
105
                        }
106
                    }
107

108
                    if (includeMoves)
1,008✔
109
                    {
110
                        var leftCommon = leftParent.Intersect(rightParent).ToList();
1,005✔
111
                        var rightCommon = rightParent.Intersect(leftParent).ToList();
1,005✔
112
                        var leftPos = leftCommon.IndexOf(key);
1,005✔
113
                        var rightPos = rightCommon.IndexOf(key);
1,005✔
114
                        if (leftPos != rightPos)
1,005✔
115
                        {
116
                            // Move
117
                            if (diffs.TryGetValue(key, out var d))
×
118
                            {
119
                                d.SiblingPosition = new List<int> { leftIdx, rightIdx };
×
120
                            }
121
                            else
122
                            {
123
                                var id = IdFields(keyFields, rightValue);
×
124
                                diffs[key] = new Diff("move", id, rightIdx, new List<int> { leftIdx, rightIdx });
×
125
                            }
126
                        }
127
                    }
128
                }
129
                else if (rightIdx >= 0)
15✔
130
                {
131
                    // Add
132
                    var child = string.Join("~", keyVals.Skip(parentFieldCount));
15✔
133
                    if (potentialMoves.TryGetValue(child, out var potentialMovesList) && potentialMovesList.Count > 0)
15✔
134
                    {
135
                        var oldKey = potentialMovesList[potentialMovesList.Count - 1];
4✔
136
                        potentialMovesList.RemoveAt(potentialMovesList.Count - 1);
4✔
137
                        diffs.Remove(oldKey);
4✔
138
                        if (includeUpdates && diffFieldsNoKeys.Length > 0)
4✔
139
                        {
140
                            leftValue = leftValues[oldKey];
4✔
141
                            var id = IdFields(right.ChildFields.ToArray(), rightValue);
4✔
142
                            var changes = DiffRow(leftValue, rightValue, left.ParentFields.Concat(diffFieldsNoKeys).ToArray(), caseSensitive, equalityProcs);
4✔
143
                            
144
                            diffs[key] = new Diff("update", id.Union(changes).ToDictionary(x => x.Key, x => x.Value), rightIdx, rightIdx);
20✔
145
                        }
146
                    }
147
                    else if (includeAdds)
11✔
148
                    {
149
                        diffs[key] = new Diff("add", rightValue, rightIdx, rightIdx);
11✔
150
                    }
151
                }
152
            }
153

154
            return diffs;
5✔
155
        }
156

157
        // Identifies the fields that are different between two versions of the
158
        // same row.
159
        private IDictionary<string, object> DiffRow(
160
            IDictionary<string, object> leftRow,
161
            IDictionary<string, object> rightRow,
162
            string[] fields,
163
            bool caseSensitive,
164
            IDictionary<string, Func<object, object, bool>> equalityProcs)
165
        {
166
            var diffs = new Dictionary<string, object>();
1,012✔
167
            foreach (var attr in fields)
24,036✔
168
            {
169
                var eqProc = equalityProcs.ContainsKey(attr) ? equalityProcs[attr] : null;
11,006✔
170
                rightRow.TryGetValue(attr, out var rightVal);
11,006✔
171
                leftRow.TryGetValue(attr, out var leftVal);
11,006✔
172

173
                if (eqProc != null)
11,006✔
174
                {
175
                    if (!eqProc(leftVal, rightVal))
×
176
                    {
177
                        diffs[attr] = new object[] { leftVal, rightVal };
×
178
                    }
179
                }
180
                else
181
                {
182
                    if (caseSensitive)
11,006✔
183
                    {
184
                        if (!string.Equals(leftVal?.ToString(), rightVal?.ToString(), StringComparison.Ordinal))
11,001✔
185
                        {
186
                            diffs[attr] = new object[] { leftVal, rightVal };
6✔
187
                        }
188
                    }
189
                    else
190
                    {
191
                        if (!string.Equals(leftVal?.ToString(), rightVal?.ToString(), StringComparison.OrdinalIgnoreCase))
5✔
192
                        {
193
                            diffs[attr] = new object[] { leftVal, rightVal };
2✔
194
                        }
195
                    }
196
                }
197
            }
198

199
            return diffs;
1,012✔
200
        }
201

202
        // Return a dictionary containing just the key field values
203
        private Dictionary<string, object> IdFields(string[] keyFields, IDictionary<string, object> fields)
204
        {
205
            var id = new Dictionary<string, object>();
8✔
206
            foreach (var field in keyFields)
40✔
207
            {
208
                if (fields.TryGetValue(field, out var value))
12✔
209
                {
210
                    id[field] = value;
12✔
211
                }
212
            }
213

214
            return id;
8✔
215
        }
216
    }
217
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc