質問

I have a list, which contains multi-level nested lists. Each list can have strings and other type instances.

E.g.

var list = [ 'a', 'w', ['e', ['f', new Object(), 'f'], 'g'], 't', 'e'];

I want to write a function (say compress) to combine strings with their siblings, and leave other type instances untouched, and finally, get a list which doesn't have nested list.

compress(list) { // how to ? }

And the result of compress(list) will be:

['awef', new Object(), 'fgte']

Is it quick and clear solution for it?

役に立ちましたか?

解決

Terse and functional FTW

List compress(Iterable iterable) => concat(flatten(iterable));

Iterable flatten(Iterable iterable) => 
    iterable.expand((e) => e is Iterable ? flatten(e) : [e]);

List concat(Iterable iterable) => iterable.fold([], (list, e) => 
    list..add((e is String && list.isNotEmpty && list.last is String)
        ? list.removeLast() + e : e));

他のヒント

This consists of two problems, a very standard one (flattening a list) and then joining the strings adequately.

flatten(Iterable l) => l.fold([], (List list, element) {
  if (element is Iterable)
    list.addAll(flatten(element));
  else
    list.add(element);
  return list;
});

concat(Iterable l) => l.fold([], (List list, element) {
  if (element is String && !list.isEmpty && list.last is String)
    list.add(list.removeLast() + element);
  else
    list.add(element);
  return list;
});

void main() {
  var nested = [ 'a', 'w', ['e', ['f', new Object(), 'f'], 'g'], 't', 'e'];
  print(concat(flatten(nested));
}

Update:

An alterantive concat, inspired by Greg Lowe (doing exactly the same like my "large" and his one, but even more condense):

concat(Iterable list) => list.fold([], (List xs, x) => xs..add(
    x is String && !xs.isEmpty && xs.last is String ? xs.removeLast() + x : x));

You can combine his and my functions as you want, they're doing exactly the same thing.

You might want to use a StringBuffer for the concatenation if you have a lot of strings. My guess is that in most cases it is not a problem.

List compress(List list) {
  var sb = new StringBuffer();
  List result = [];
  List compressRec(List list) {
    for (var element in list) {
      if (element is String) {
        sb.write(element);
      } else if (element is List) {
          compressRec(list);
      } else {
        if (sb.isNotEmpty()) {
          result.add(sb.toString());
          sb.clear();
        }
        result.add(element);
      }
    }
  }
  compressRec(list)
  if (sb.isNotEmpty()) {
    result.add(sb.toString());
  }
  return result;
}

Here's my guess :

compress(List l, [List p]) => l.fold(p != null ? p : [], (List t, e) {
  if (e is String) {
    if (t.isEmpty || t.last is! String) t.add(e);
    else t.add(t.removeLast() + e);
  } else if (e is List) compress(e, t);
  else t.add(e);
  return t;
});

My attempt

List flatten(List l) {
  List result = [''];
  int cur = 0;

  var add = (f) {
    if(f is String) {
      result[cur] += f;
    } else {
      result.add(f);
      result.add('');
      cur += 2;
    }
  };

  l.forEach((e) {
    if(e is List) {
      flatten(e).forEach((e) => add(e));
    } else {
      add(e);
    }
  });
  return result;
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top