Compare list of objects without equal defined - groovy

I have a class without equal function defined. I need to compare two lists of this objects. There is some sample of my current solution:
def fromRecords = [new FooRecord(['ABC', 123L]), new FooRecord(['XYZ', 789L])]
storage.write(fromRecords)
def toRecords = storage.read()
for (int i = 0; i < outRecords.size(); i++) {
assert outRecords[i].contents == records[i].contents
}
Is there more elegant groovy way to perform same comparison?

You don't need to loop, the equals method on the lists should take care of comparing order and check element-wise equality:
assert outRecords.contents == records.contents
The == operator will result in outRecords.contents.equals(records.contents) being called, which will check List equality (taking care of order and list content - element by element .equals checks).

Related

Create Map with CollectEntries in Groovy

I have the following list:
appList = [DevOpsApplication, 01.01.01]
I would like to create a map using collectEntries. I know that it refers to the current element of an iteration (shortcut for { it -> it }). Therefore, I tried to use the index:
def appMap = appList.collectEntries { [(it[0]):it[1]] }
However, this gives me:
[D:e, 0:1]
But I want [DevOpsApplication: 01.01.01]. Is there a way to do this?
Additionally, In future I would like this to expand to more than 2 elements (e.g. [DevOpsApplication, 01.01.01, AnotherDevOpsApplication, 02.02.02]) with the desired output of [DevOpsApplication: 01.01.01, AnotherDevOpsApplication: 02.02.02].
How will this be possible?
A very short version to do this would be:
def appList = ["DevOpsApplication", "01.01.01"]
def appMap = [appList].collectEntries() // XXX
assert appMap == [DevOpsApplication: "01.01.01"]
How does it work: the function collectEntries takes, is expected to return a map or a two element list. Your appList is already that. So put that in another list, call collectEntries on it. When no function is given to collectEntries it uses the identity function.
Bonus: what if appList has much more elements? You can use collate to build the tuples.
def appList = ["DevOpsApplication", "01.01.01", "Some", "More"]
def appMap = appList.collate(2).collectEntries() // XXX
assert appMap == [DevOpsApplication: "01.01.01", Some: "More"]
I also found another method. Groovy can convert the values of an Object array and convert them into a map with the toSpreadMap(). However, the array must have an even number of elements.
def appList = ['DevOpsApplication', '01.01.01']
def appMap = appList.toSpreadMap()
You're iterating element-by-element and (because your elements are String-typed) mapping 0 substrings to 1 substrings.
You can use this to skip one element in each iteration and map each element at even indices to the one after it:
def appList = ['DevOpsApplication', '01.01.01']
def appMap = (0..(appList.size()-1)).findAll{0 == it%2}
.collectEntries{[(appList[it]): appList[it+1]]}
That returns [DevOpsApplication:01.01.01] as expected.

Given a string sequence of words, check if it matches a pattern

I encountered this problem in an interview and I was stuck on the best way to go about it. The question is as follows:
Given a string sequence of words and a string sequence pattern, return true if the sequence of words matches the pattern otherwise false.
Definition of match: A word that is substituted for a variable must always follow that substitution. For example, if "f" is substituted as "monkey" then any time we see another "f" then it must match "monkey" and any time we see "monkey" again it must match "f".
Examples
input: "ant dog cat dog", "a d c d"
output: true
This is true because every variable maps to exactly one word and vice verse.
a -> ant
d -> dog
c -> cat
d -> dog
input: "ant dog cat dog", "a d c e"
output: false
This is false because if we substitute "d" as "dog" then you can not also have "e" be substituted as "dog".
a -> ant
d, e -> dog (Both d and e can't both map to dog so false)
c -> cat
input: "monkey dog eel eel", "e f c c"
output: true
This is true because every variable maps to exactly one word and vice verse.
e -> monkey
f -> dog
c -> eel
Initially, I thought of doing something as follows...
function matchPattern(pattern, stringToMatch) {
var patternBits = pattern.split(" ");
var stringBits = stringToMatch.split(" ");
var dict = {};
if (patternBits.length < 0
|| patternBits.length !== stringBits.length) {
return false;
}
for (var i = 0; i < patternBits.length; i++) {
if (dict.hasOwnProperty(patternBits[i])) {
if (dict[patternBits[i]] !== stringBits[i]) {
return false;
}
} else {
dict[patternBits[i]] = stringBits[i];
}
}
return true;
}
var ifMatches = matchPattern("a e c d", "ant dog cat dog");
console.log("Pattern: " + (ifMatches ? "matches!" : "does not match!"));
However, I realized that this won't work and fails example #2 as it erroneously returns true. One way to deal with this issue is to use a bi-directional dictionary or two dictionaries i.e store both {"a": "ant"} and
{"ant": "a"} and check both scenarios in the if check. However, that seemed like wasted space. Is there a better way to tackle this problem without using regular expressions?
I think a simple choice that is quadratic in the length of the list of words is to verify that every pairing of list indices has the same equality characteristics in the two lists. I'll assume that you get the "words" and "pattern" as lists already and don't need to parse out spaces and whatever -- that ought to be a separate function's responsibility anyway.
function matchesPatternReference(words, pattern) {
if(words.length !== pattern.length) return false;
for(var i = 0; i < words.length; i++)
for(var j = i+1; j < words.length; j++)
if((words[i] === words[j]) !== (pattern[i] === pattern[j]))
return false;
return true;
}
A slightly better approach would be to normalize both lists, then compare the normalized lists for equality. To normalize a list, replace each list element by the number of unique list elements that appear before its first occurrence in the list. This will be linear in the length of the longer list, assuming you believe hash lookups and list appends take constant time. I don't know enough Javascript to know if these are warranted; certainly at worst the idea behind this algorithm can be implemented with suitable data structures in n*log(n) time even without believing that hash lookups are constant time (a somewhat questionable assumption no matter the language).
function normalize(words) {
var next_id = 0;
var ids = {};
var result = [];
for(var i = 0; i < words.length; i++) {
if(!ids.hasOwnProperty(words[i])) {
ids[words[i]] = next_id;
next_id += 1;
}
result.push(ids[words[i]]);
}
return result;
}
function matchesPatternFast(words, pattern) {
return normalize(words) === normalize(pattern);
}
Note: As pointed out in the comments, one should check deep equality of the normalized arrays manually, since === on arrays does an identity comparison in Javascript and does not compare elementwise. See also How to check if two arrays are equal with Javascript?.
Addendum: Below I argue that matchesPatternFast and matchesPatternReference compute the same function -- but use the faulty assumption that === on arrays compares elements pointwise rather than being a pointer comparison.
We can define the following function:
function matchesPatternSlow(words, pattern) {
return matchesPatternReference(normalize(words), normalize(pattern));
}
I observe that normalize(x).length === x.length and normalize(x)[i] === normalize(x)[j] if and only if x[i] === x[j]; therefore matchesPatternSlow computes the same function as matchesPatternReference.
I will now argue that matchesPatternSlow(x,y) === matchesPatternFast(x,y). Certainly if normalize(x) === normalize(y) then we will have this property. matchesPatternFast will manifestly return true. On the other hand, matchesPatternSlow operates by making a number of queries on its two inputs and verifying that these queries always return the same results for both lists: outside the loop, the query is function(x) { return x.length }, and inside the loop, the query is function(x, i, j) { return x[i] === x[j]; }. Since equal objects will respond identically to any query, it follows that all queries on the two normalized lists will align, matchesPatternSlow will also return true.
What if normalize(x) !== normalize(y)? Then matchesPatternFast will manifestly return false. But if they are not equal, then either their lengths do not match -- in which case matchesPatternSlow will also return false from the first check in matchesPatternReference as we hoped -- or else the elements at some index are not equal. Suppose the smallest mismatching index is i. It is a property of normalize that the element at index i will either be equal to an element at index j<i or else it will be one larger than the maximal element from indices 0 through i-1. So we now have four cases to consider:
We have j1<i and j2<i for which normalize(x)[j1] === normalize(x)[i] and normalize(y)[j2] === normalize(y)[i]. But since normalize(x)[i] !== normalize(y)[i] we then know that normalize(x)[j1] !== normalize(y)[i]. So when matchesPatternReference chooses the indices j1 and i, we will find that normalize(x)[j1] === normalize(x)[i] is true and normalize(y)[j1] === normalize(y)[i] is false and immediately return false as we are trying to show.
We have j<i for which normalize(x)[j] === normalize(x)[i] and normalize(y)[i] is not equal to any previous element of normalize(y). Then matchesPatternReference will return false when it chooses the indices j and i, since normalize(x) matches on these indices but normalize(y) doesn't.
We have j<i for which normalize(y)[j] === normalize(y)[i] and normalize(x)[i] is not equal to any previous element of normalize(x). Basically the same as in the previous case.
We have that normalize(x)[i] is one larger than the largest earlier element in normalize(x) and normalize(y)[i] is one larger than the largest earlier element in normalize(y). But since normalize(x) and normalize(y) agree on all previous elements, this means normalize(x)[i] === normalize(y)[i], a contradiction to our assumption that the normalized lists differ at this index.
So in all cases, matchesPatternFast and matchesPatternSlow agree -- hence matchesPatternFast and matchesPatternReference compute the same function.
For this special case, I assume the pattern refers to matching first character. If so, you can simply zip and compare.
# python2.7
words = "ant dog cat dog"
letters = "a d c d"
letters2 = "a d c e"
def match(ws, ls):
ws = ws.split()
ls = ls.split()
return all(w[0] == l for w, l in zip(ws + [[0]], ls + [0]))
print match(words, letters)
print match(words, letters2)
The funny [[0]] and [0] in the end is to ensure that the pattern and the words have the same length.

Python - Create a recursion function

my question is basically this: Create a recursion function that takes a nested list as a
parameter and returns the sub-list that has minimum difference between its maximum and minimum elements.
For example: Function should return [1,2] for input [[1,199,59],[1,2],[3,8]]
I searched Google and stackoverflow, but i could not find this specific example.
What i would like to get help is with iteration. I want to, using recursion, iterate over each sub-list(can be as many as possible). I have achieved this with a for loop, but i cannot grasp the idea of iteration by using recursion method.
So far, i have this:
def sublist(mylist):
if len(mylist) == 0:
return []
elif len(mylist) == 1:
return mylist
else:
a = (mylist[0][0]) - (mylist[0][-1])
if a < sublist(mylist[1:]):
return mylist[0]
sublist([[1,199,58],[1,2],[3,8]])
This part, ( sublist(mylist[1:]) ) i know is clearly wrong. I'm trying to compare the value a, with the values from the mylist[1:]. I would appreciate much advice here.
Updated:
def differences(mylist):
diff = max(mylist) - min(mylist)
return diff
def sublist(nestedlist):
if len(nestedlist) == 1:
return nestedlist[0]
else:
if differences(nestedlist[0]) < differences(sublist(nestedlist[1:])):
return nestedlist[0]
else:
return sublist(nestedlist[1:])
print(sublist([[1,199,59],[1,2],[3,8]]))
i am assuming that you want to use recursion for the first level of the list. So, without giving you the code 100%, you have to do something like that:
1) create a method e.g diferences(list) that calculates the differences of a list and returns a list with the parameter list and the min difference i.e differences([1,2]) should return [1, [1,2]]. call it once on the first sublist i.e min = differences(mylist[0])
2) create your sublist method like this:
def sublist(initial_list):
# 1) call differences() method for the first sublist of the 'initial_list'
# 2) update 'min' with differences(initial_list[0])if differences(inilitial_list[0])[0] < min[0];
# 3) call sublist() again now removing the sublist you checked before from the arguement
# 4) (the following should be at the start of your sublist() method)
if len(initial_list) = 1:
if differences(initial_list) < min:
return initial_list
else: return min[1]
Hope that helps

Get any consecutive sequence containing the given number of elements that matches a fixed condition from a list C#

I have a list defined as below inside a method
List<string> testList = new List<string>() {"A","A","B","A","A","A","B"};
Here my condition will be fixed say where the element matches "A". Based on upon my input, for instance 2, logic should identify two consecutive A's in the list and return them. In the above list it should return first and second elements. If my input is 3 it should return fourth,fifth and sixth elements where three A's are consecutive. If it is 4 it should not return anything. Is there a simple way of implementing this in C#
This is one way to do what you want:
List<string> testList = new List<string>() {"A","A","B","A","A","A","B"};
string inputText = <your input text>;
int inputCount = <your input count>;
var zipped = testList.Zip(Enumerable.Range(0,testList.Count-1), (txt,idx) => new {txt,idx});
var result = zipped
.Where(combined => combined.idx <= testList.Count-inputCount)
.Where(combined => combined.txt == inputText
&& testList.GetRange(combined.idx, inputCount).All(z => z == inputText));
We zip with the ordered range [0..list count - 1] to provide indexes for your elements. Then we check if current element in the collection equals the input text, and if the first n elements starting from current one all equal input text, where n is the input count.
We can then use a foreach loop to iterate through result like so:
foreach(var r in result)
{
testList.GetRange(r.idx,inputCount).ForEach(x => Console.WriteLine(x));
}
For each pattern that is found, we use GetRange to get the corresponding values.
Note that we are creating another IEnumerable object when we call Zip, and also calling GetRange within the Where clause, so I don't think this method is very efficient. Nevertheless, it will solve the issue.

Safe range operator in Groovy?

Is there a safe range operator for Groovy?
For instance if I have,
[1,2,3][0..10]
Groovy will throw a java.lang.IndexOutOfBoundsException:
Is there a index safe way to access this range? Or do I always have to check the collection size prior to running a range?
You can use take(n), which allows you to take up to a specific number of items, without error if there's too few in the collection:
def input = [1,2,3]
def result = input.take(10)
assert result == [1,2,3]
input = [1,2,3,4,5]
result = input.take(4)
assert result == [1,2,3,4]
If you need to start at an offset, you can use drop(n), which does not modify the original collection:
def input = [1,2,3,4,5]
def result = input.drop(2).take(2)
assert result == [3,4]
These are both safe against the size of the collection. If the list is too small in the last example, you may only have one or zero items in the collection.

Resources