0

I have already a list of directory's paths. For example:

/home/aar/las
/home/aar/las/pes
/home/aar/las/fmp
/home/aar/.thumbnails
/home/aar/.thumbnails/normal
/home/aar/yfolder
/home/aar/.kde
/home/aar/.kde/share
/home/aar/.kde/share/config
/home/aar/.kde/share/apps
/home/aar/.kde/share/apps/okular

I want to remove all subdirectories and keep the parents only.

To do this, I used this code that's works fine:

QStringList RemoveSubFolders_KeepParentFolders(QStringList FoldersList)
{
    QStringList FoldersList_First = FoldersList;
    QStringList FoldersList_Second = FoldersList;

    QStringList ToDelete;
    foreach (QString TheFolder_First, FoldersList_First) {
        foreach (QString TheFolder_Second, FoldersList_Second) {
            if (TheFolder_Second.startsWith(TheFolder_First) && TheFolder_First.toLower() != TheFolder_Second.toLower())
                ToDelete.append(TheFolder_Second);
        }
    }

    QStringListIterator i(ToDelete);
     while(i.hasNext()){
       FoldersList.removeAll(i.next());
     }

     return FoldersList;  //return list without subfolders
}

The Result is:

/home/aar/las
/home/aar/.thumbnails
/home/aar/yfolder
/home/aar/.kde

========================================================

The question:

Eventhough, I think that is not the the best and fastest way for a large list.

Is there an best way to do the job?

NB:

  • I don't care about Windows or Ubunutu.
  • I use Qt 5.8 c++.

Thank you.

Doros Barmajia
  • 502
  • 1
  • 5
  • 13
  • **(1)** Would subdirectories always immediately follow directories in the list, as in your example? In other words, is `/a ; /b ; /a/x` possible? **(2)** Does the order in the final list matter? In other words, if you start with `/b ; /b/x ; /a` is it OK to end up with `/a ; /b` as opposed to `/b ; /a`? – Igor Tandetnik Mar 04 '17 at 14:43
  • @IgorTandetnik (1) I'm not sure (2) The order will be a good thing but it is not necessary. – Doros Barmajia Mar 04 '17 at 14:50
  • You can copy folders that have the least `/` in their path. – tnt Mar 04 '17 at 15:36

2 Answers2

0

Something along these lines (using std::vector and std::string in place of QStringList and QString; adapting to Qt classes is left as an exercise for the reader):

std::vector<std::string> v;  // populated with directory paths
std::sort(v.begin(), v.end());

std::string root;
auto erase_from = std::unique(v.begin(), v.end(),
  [&root](const std::string& a, const std::string& b) {
    if (root.empty() || a.size() < root.size() || a.compare(0, root.size(), root) != 0) {
      root = a;
    }
    return (b.size() >= root.size() && b.compare(0, root.size(), root) == 0);
  }
);
v.erase(erase_from, v.end());

Demo


On second thought, using std::remove_if would be simpler and more efficient:

std::vector<std::string> v;  // populated with directory paths
std::sort(v.begin(), v.end());

std::string root;
auto erase_from = std::remove_if(v.begin(), v.end(),
  [&root](const std::string& dir) {
    if (root.empty() || dir.size() < root.size() ||
        dir.compare(0, root.size(), root) != 0) {
      root = dir;
      return false;
    }
    return true;
  }
);
v.erase(erase_from, v.end())

Demo

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
0

This is a hack: keep paths that have the least / in them

int pathDepth(const QString& path)
{
    return std::count_if(path.begin(), path.end(), [](QChar c) { return c == '/'; });
}

QStringList getParentFolders(const QStringList& folderList)
{
    QStringList ret;
    int minDepth = pathDepth(*std::min_element(folderList.begin(), folderList.end(),
        [](const QString& a, const QString& b){ return pathDepth(a) < pathDepth(b); }));
    foreach (QString path, folderList)
        if (pathDepth(path) == minDepth)
            ret << path;
    return ret;
}
tnt
  • 1,174
  • 2
  • 10
  • 14