I have a list of elements and I want to remove one of them, by value. In Python this would be
l = ["apples", "oranges", "melon"]
l.remove("melon")
print(l) # ["apples", "orange"]
What is the equivalent in Go? I found a slice trick to remove an element by index, but it's not very readable, still requires me to find the index manually and only works for a single item type:
func remove(l []string, item string) {
for i, other := range l {
if other == item {
return append(l[:i], l[i+1:]...)
}
}
}
There's the list.List structure, but it's not generic and thus requires tons of type-castings to use.
What is the idiomatic way of removing an element from a list?
The idiomatic way to remove an element from a list is to loop through it exactly like you do in your example. Removing an element by value from a slice shouldn't be too common in your program since it is an O(n) operation and there are better data structures in the language for that. Therefore, Go does not provide a built-in remove function for slices.
If you find yourself using removal by value often, consider using a set instead where removing and adding an element is O(1) while still being iterable.
set := map[string]bool{"apples":true, "oranges":true, "melon":true}
delete(set,"melon") // is O(1)
In generic Go (1.18) the filter function works on any comparable type. It removes only the first occurrence of the item.
func remove[T comparable](l []T, item T) []T {
for i, other := range l {
if other == item {
return append(l[:i], l[i+1:]...)
}
}
return l
}
Playground: https://go.dev/play/p/ojlYkvf5dQG?v=gotip
If you want to remove all occurrences, append the items that don't match to a new slice:
func remove[T comparable](l []T, item T) []T {
out := make([]T, 0)
for _, element := range l {
if element != item {
out = append(out, element)
}
}
return out
}
Playground: https://go.dev/play/p/W2MerNbh72H
If the slice items aren't comparable, you can use a custom equality function to filter:
func main() {
list := [][]int{{1, 2}, {2, 2}, {1, 10}}
newlist := remove(list, func(element []int) bool { return element[0] == 1 })
fmt.Println(newlist)
}
func remove[T any](l []T, remove func(T) bool) []T {
out := make([]T, 0)
for _, element := range l {
if !remove(element) {
out = append(out, element)
}
}
return out
}
Playground: https://go.dev/play/p/qZWoFbM_RUl
Related
In a test function, there is a case where nested slices should be compared.
Say I have two varibles like the following:
want := [][]string{{"bat"},{"nat","tan"},{"ate","eat","tea"}}
got := [][]string{{"eat","tea","ate"},{"tan","nat"},{"bat"}}
How can compare them?
First, I used reflect.DeepEqual which was wrong, I also tried go-cmp:
t.Run(tt.name, func(t *testing.T) {
opt := cmpopts.SortSlices(func (a, b []int) bool {
// not sure what to write
})
if got := groupAnagrams(tt.args.strs); !cmp.Equal(got, tt.want, opt) {
t.Errorf("groupAnagrams() = %v, want %v", got, tt.want)
}
})
Sort the inner slices:
for _, s := range want { sort.Strings(s) }
for _, s := range got { sort.Strings(s) }
Sort the outer slices:
sortOuter(want)
sortOuter(got)
where sortOuter is a the function:
func sortOuter(s [][]string) {
sort.Slice(s, func(a, b int) bool {
sa := s[a]
sb := s[b]
n := len(sa)
if n > len(sb) {
n = len(sb)
}
for i := 0; i < n; i++ {
if sa[i] != sb[i] {
return sa[i] < sb[i]
}
}
return len(sa) < len(sb)
})
}
Compare:
fmt.Println(reflect.DeepEqual(got, want))
https://go.dev/play/p/SjN8gLmotjd
You can sort inner slices using sort.Slice as below and check if outer slices are equal using testify assert.ElementsMatch:
func TestXxx(t *testing.T) {
// Slices
want := [][]string{{"bat"}, {"nat", "tan"}, {"ate", "eat", "tea"}}
got := [][]string{{"eat", "tea", "ate"}, {"tan", "nat"}, {"bat"}}
// Running tests
t.Run("test", func(t *testing.T) {
// Sorting got inners
for _, inner := range got {
sort.Slice(inner, func(i, j int) bool {
return inner[i] < inner[j]
})
}
// Sorting want inners
for _, inner := range want {
sort.Slice(inner, func(i, j int) bool {
return inner[i] < inner[j]
})
}
// Match
assert.ElementsMatch(t, got, want)
})
}
ElementsMatch:
ElementsMatch asserts that the specified listA(array, slice...) is equal to specified listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, the number of appearances of each of them in both lists should match.
assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])
I have a List of objects I want to split by a delimiter into sublists, e.g:
val tokens = listOf(
Token(name = "lorem", val = "ipsum"),
Token(name = "dolor", val = "sit"),
Token(name = "newline", val = "\n"),
Token(name = "amet", val = "consectetur")
)
The delimiter should be any Token whose name is "newline", so after the split, tokens should become:
listOf(
listOf(
Token(name = "lorem", val = "ipsum"),
Token(name = "dolor", val = "sit")
),
listOf(
Token(name = "amet", val = "consectetur")
)
)
I've written my own function to do this already, but is there some elegant, built-in (preferably functional) way of doing it? I say this because I'm learning Kotlin and, coming from C++, find myself "reinventing the wheel" a lot with these types of things.
In such cases I suggest not going with too much functional transformations. We can for example do it by folding/reducing, we can also first find indices of all delimiters and then zipWithNext() between them to get ranges, etc. This way we get the solution in a very few lines of code, but this code will be very hard to read and understand.
Instead, I suggest going with a good old and very simple loop. However, to make it smoother and more performant, we can use sequences and subList():
fun main() {
val tokens = ...
tokens.splitBy { it.name == "newline" }
}
fun <T> List<T>.splitBy(predicate: (T) -> Boolean): Sequence<List<T>> = sequence {
if (isEmpty()) return#sequence
var last = 0
forEachIndexed { i, v ->
if (predicate(v)) {
yield(subList(last, i))
last = i + 1
}
}
yield(subList(last, size))
}
Please note this solution does not involve any data copying. It iteratively creates views of the original list, so it should be pretty fast. On the other hand, it should be used with care if the original list may change.
Also, you need to be aware of corner cases like: delimiter at the beginning and end, no delimiters in the list or empty list. There is no single answer to how splitting should work in these cases. Whatever solution you pick, I suggest checking it for these cases. My above solution mirrors the behavior of String.split().
I think there isn't any extension function in standard library for handling this case. You will have to write your own logic. You can do something like this:
val newList = mutableListOf<List<Token>>()
var subList = mutableListOf<Token>()
for (token in tokens) {
if (token.name == "newline") {
newList += subList
subList = mutableListOf()
} else {
subList += token
}
}
if (subList.isNotEmpty())
newList += subList
println(newList)
You can also extract this code out in the form of an extension function:
fun <T> List<T>.split(delimiter: (T) -> Boolean): List<List<T>> {
val newList = mutableListOf<List<T>>()
var subList = mutableListOf<T>()
for (token in this) {
if (delimiter(token)) {
newList += subList
subList = mutableListOf()
} else {
subList += token
}
}
if (subList.isNotEmpty())
newList += subList
return newList
}
// Usage
fun main() {
val tokens = listOf(
Token(name = "lorem", val = "ipsum"),
Token(name = "dolor", val = "sit"),
Token(name = "newline", val = "\n"),
Token(name = "amet", val = "consectetur")
)
println(tokens.split { it.name == "newline" })
}
You can use fold:
tokens
.fold(mutableListOf(mutableListOf<Token>())) { l, elem ->
if(elem.name=="newline") l.add(mutableListOf())
else l.last().add(elem)
l
}
The first parameter is the initial value, a list with a single list in it (if there isn't any newline, you still want to have a single list containing the elements).
The second parameter is a function that is executed for every element.
If the token name is newline, it adds a new list. If not, it adds the element to the last list.
The last line of fold containing l makes sure that the list is returned.
I have two Lists and I want to get the List containing only the elements in the first list that are not in the second one. The problem is that I need to specify a custom equal when subtracting. Let's suppose I want to use one of the fields in the entries of the lists. Let's say the id.
I implemented it this way:
list1.filter { log -> list2.none { it.id == log.id } }
or
val projection = logEntries.map { it.id }
list1.filter { it.id !in projection }
Is there a better way to do it? Please take into account that I can't set a new equal method for the class.
The way you do it is ok, but when the lists become bigger you might want to do that:
You could make the process more efficient by turning your reference list (list2) to a set first.
val referenceIds = list2.distinctBy { it.id }.toSet()
list1.filterNot { it.id in referenceIds }
Background:
An ArrayList which you are most likely using has a time complexity of O(n) when you check if an element is contained. So, if the list gets bigger, it will take longer.
A HashSet on the other hand has a time complexity of O(1) when checking if an element is contained. So, if list2 becomes bigger it won't get slower.
Another approach?
fun main() {
val list1 = listOf(0, 1, 2, 3, 4, 5)
val list2 = listOf(2,3,4)
println(list1.filterNotIn(list2))
}
fun <T> Collection<T>.filterNotIn(collection: Collection<T>): Collection<T> {
val set = collection.toSet()
return filterNot { set.contains(it) }
}
Output: [0, 1, 5]
As per comments, there's no built-in way to do this. (Probably not for any fundamental reason; just because no-one saw a need.)
However, you can easily add one yourself. For example, here's your first suggestion, converted to extension function:
fun <T, R> Collection<T>.minus(elements: Collection<T>, selector: (T) -> R?)
= filter{ t -> elements.none{ selector(it) == selector(t) } }
You could then call this in the same way that a built-in function would work:
list1.minus(list2){ it.id }
(There are probably more efficient implementations, but this illustrates the idea.)
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/minus.html
fun main() {
val list1 = listOf(0, 1, 2, 3, 4, 5)
val list2 = listOf(2,3,4)
println(list1.minus(list2))
}
What would be an idiomatic way to create a mutable list of a given length n with repeating elements of value v (e.g listOf(4,4,4,4,4)) as an expression.
I'm doing val list = listOf((0..n-1)).flatten().map{v} but it can only create an immutable list.
Use:
val list = MutableList(n) {index -> v}
or, since index is unused, you could omit it:
val list = MutableList(n) { v }
another way may be:
val list = generateSequence { v }.take(4).toMutableList()
This style is compatible with both MutableList and (Read Only) List
If you want different objects you can use repeat.
For example,
val list = mutableListOf<String>().apply {
repeat(2){ this.add(element = "YourObject($it)") }
}
Replace String with your object. Replace 2 with number of elements you want.
You're able to use the ranges for this purpose, e.g.
val listOfFour = (1..10).map { 4 }
or
val objectList = (1..10).map {
YourClass(
arg1 = "someValue",
arg2 = it
)
}
if you need you can use it (index) for your needs as well.
I'm comparing two lists in Dart like this:
main() {
if ([1,2,3] == [1,2,3]) {
print("Equal");
} else {
print("Not equal");
}
}
But they are never equal. There doesn't seem to be an equal() method in the Dart API to compare Lists or Collections. Is there a proper way to do this?
To complete Gunter's answer: the recommended way to compare lists for equality (rather than identity) is by using the Equality classes from the following package
import 'package:collection/collection.dart';
Edit: prior to 1.13, it was import 'package:collection/equality.dart';
E.g.:
Function eq = const ListEquality().equals;
print(eq([1,'two',3], [1,'two',3])); // => true
The above prints true because the corresponding list elements that are identical(). If you want to (deeply) compare lists that might contain other collections then instead use:
Function deepEq = const DeepCollectionEquality().equals;
List list1 = [1, ['a',[]], 3];
List list2 = [1, ['a',[]], 3];
print( eq(list1, list2)); // => false
print(deepEq(list1, list2)); // => true
There are other Equality classes that can be combined in many ways, including equality for Maps. You can even perform an unordered (deep) comparison of collections:
Function unOrdDeepEq = const DeepCollectionEquality.unordered().equals;
List list3 = [3, [[],'a'], 1];
print(unOrdDeepEq(list2, list3)); // => true
For details see the package API documentation. As usual, to use such a package you must list it in your pubspec.yaml:
dependencies:
collection: any
For those using Flutter, it has the native function listEquals, which compares for deep equality.
import 'package:flutter/foundation.dart';
var list1 = <int>[1, 2, 3];
var list2 = <int>[1, 2, 3];
assert(listEquals(list1, list2) == true);
import 'package:flutter/foundation.dart';
var list1 = <int>[1, 2, 3];
var list2 = <int>[3, 2, 1];
assert(listEquals(list1, list2) == false);
Note that, according to the documentation:
The term "deep" above refers to the first level of equality: if the elements are maps, lists, sets, or other collections/composite objects, then the values of those elements are not compared element by element unless their equality operators (Object.operator==) do so.
Therefore, if you're looking for limitless-level equality, check out DeepCollectionEquality, as suggested by Patrice Chalin.
Also, there's setEquals and mapEquals if you need such feature for different collections.
Collections in Dart have no inherent equality. Two sets are not equal, even if they contain exactly the same objects as elements.
The collection library provides methods to define such an equality. In this case, for
example
IterableEquality().equals([1,2,3],[1,2,3])
is an equality that considers two lists equal exactly if they contain identical elements.
I just stumbled upon this
import 'package:collection/equality.dart';
void main(List<String> args) {
if (const IterableEquality().equals([1,2,3],[1,2,3])) {
// if (const SetEquality().equals([1,2,3].toSet(),[1,2,3].toSet())) {
print("Equal");
} else {
print("Not equal");
}
}
more info https://github.com/dart-lang/bleeding_edge/tree/master/dart/pkg/collection
You can use the built-in listEquals, setEquals, and mapEquals function to check for equality now.
Here's the link to the docs: https://api.flutter.dev/flutter/foundation/listEquals.html
While this question is rather old, the feature still hasn't landed natively in Dart. I had a need for deep equality comparison (List<List>), so I borrowed from above now with recursive call:
bool _listsAreEqual(list1, list2) {
var i=-1;
return list1.every((val) {
i++;
if(val is List && list2[i] is List) return _listsAreEqual(val,list2[i]);
else return list2[i] == val;
});
}
Note: this will still fail when mixing Collection types (ie List<Map>) - it just covers List<List<List>> and so-on.
Latest dart issue on this seems to be https://code.google.com/p/dart/issues/detail?id=2217 (last updated May 2013).
A simple solution to compare the equality of two list of int in Dart can be to use Sets (without checking the order of the element in the list) :
void main() {
var a = [1,2,3];
var b = [1,3,2];
var condition1 = a.toSet().difference(b.toSet()).isEmpty;
var condition2 = a.length == b.length;
var isEqual = condition1 && condition2;
print(isEqual); // Will print true
}
The Built Collections library offers immutable collections for Dart that include equality, hashing, and other goodies.
If you're doing something that requires equality there's a chance you'd be better off with immutability, too.
http://www.dartdocs.org/documentation/built_collection/0.3.1/index.html#built_collection/built_collection
Sometimes one want to compare not the lists themselves but some objects (class instances) that contain list(s)
Let you have a class with list field, like
class A {
final String name;
final List<int> list;
A(this.name, this.list);
}
and want to compare its instances, you can use equatable package
class A extends Equatable {
final String name;
final List<int> list;
A(this.name, this.list);
#override
List<Object> get props => [name, list];
}
and then simply compare objects using ==
final x = A('foo', [1,2,3]);
final y = A('foo', [1,2,3]);
print(x == y); // true
The most convenient option would be to create an extension method on the List class.
extension on List {
bool equals(List list) {
if(this.length!=list.length) return false;
return this.every((item) => list.contains(item));
}
}
What the above code does is that it checks to see if every single item from the first list (this) is contained in the second list (list). This only works, however, if both lists are of the same length. You can, of course, substitute in one of the other solutions or modify this solution, I am just showing this for the sake of convenience. Anyway, to use this extension, do this:
List list1, list2
list1.equals(list2);
Before things work, you can use this:
/**
* Returns true if the two given lists are equal.
*/
bool _listsAreEqual(List one, List two) {
var i = -1;
return one.every((element) {
i++;
return two[i] == element;
});
}
Would something like this work for you?
try {
Expect.listEquals(list1, list2);
} catch (var e) {
print("lists aren't equal: $e");
}
Example:
main() {
List<int> list1 = [1, 2, 3, 4, 5];
List<int> list2 = new List.from(list1);
try {
Expect.listEquals(list1, list2);
} catch (var e) {
print("lists aren't equal: $e");
}
list1.removeLast();
try {
Expect.listEquals(list1, list2);
} catch (var e) {
print("Lists aren't equal: $e");
}
}
Second try prints:
Lists aren't equal: Expect.listEquals(list length, expected: <4>, actual: <5>) fails
bool areListsEqual(var list1, var list2) {
// check if both are lists
if(!(list1 is List && list2 is List)
// check if both have same length
|| list1.length!=list2.length) {
return false;
}
// check if elements are equal
for(int i=0;i<list1.length;i++) {
if(list1[i]!=list2[i]) {
return false;
}
}
return true;
}
A simple solution to compare the equality of two list of int in Dart can be to compare it as String using join() :
var a = [1,2,3,0];
var b = [1,2,3,4];
a.join('') == b.join('') ? return true: return false;