Can't think my way out of this one

Morning all,

My code works, but I'm having trouble implementing some logic, let me give you the run down.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void Search(std::vector<Item>& db, std::vector<std::string> args)
{
	std::cout << "\n";
	DispHeader();
	for(unsigned int i = 0; i < db.size(); i++)
	{
		for(unsigned int j = 0; j < args.size(); j++)
		{
			if(
				db[i].StockID.find(args[j]) != std::string::npos ||
				db[i].Type.find(args[j]) != std::string::npos ||
				db[i].Area.find(args[j]) != std::string::npos ||
				db[i].Serial.find(args[j]) != std::string::npos ||
				db[i].Date.find(args[j]) != std::string::npos ||
				db[i].Make.find(args[j]) != std::string::npos ||
				db[i].Model.find(args[j]) != std::string::npos ||
				db[i].ProcessorMake.find(args[j]) != std::string::npos ||
				db[i].ProcessorSpeed.find(args[j]) != std::string::npos ||
				db[i].MemoryMake.find(args[j]) != std::string::npos ||
				db[i].MemoryAmount.find(args[j]) != std::string::npos ||
				db[i].Quantity.find(args[j]) != std::string::npos ||
				db[i].Colour.find(args[j]) != std::string::npos ||
				db[i].Status.find(args[j]) != std::string::npos
			)
			{
				std::cout << db[i];
				break;
			}
		}
	}
	std::cout << "\n";
}


Very easy function: When Search is called, the Item database (std::vector<Item>&) and Search arguments (std::vector<string>&) are passed to it. The loop iterate through all the item elements and arguments and find if they exist, if they do, they get printed out.


Here's the deal.

I want to redesign this, so each argument after the first is an && rather than an or, but I'm not sure how to implement this in a loop which has to go over multiple items and multiple arguments.

Here's an example:


> Search Laptop DELL ^D

Stock	Type   	Area     	Serial         	In-Date   	Make  	Model          	CPU  	Speed	Memory  	Amount	Qty   	Colour  	Status  
------------------------------------------------------------------------------------------------------------------------------------------------------
0001 	Laptop 	CB-2/SH-01	01AFDCFG       	01/01/2014	HP    	COMPAQ/2400T   	INTEL	2.4 	KINGSTON	2.0 	01    	BLACK   	PENDING 
0002 	Laptop 	CB-2/SH-02	011FGSGD       	02/01/2014	DELL  	INSPIRON/990   	AMD  	2.0 	ALIENWARE	4.0 	05    	WHITE   	DONE    



As you can see, both items are printed out, because both contain Laptop. I understand why this happens, it's because I use || on each loop. I want to search function to see the arguments as Laptop AND DELL.

I feel like I need some ORAND logic or something, or I'm just missing something.

Hope this has made sense.

Any help or nudge is appreciated.


EDIT:

After staring at this blankly for a few minutes, How about a little recursion?

1
2
3
4
5
6
7
...
std::vector<Item> Temp;
if(...) Temp.push_back(...); // Each Item which contains an arg is added to this
...
//When done, Search calls itself with the new list of items, and checks for the next argument on this list
args.pop_back();
Search(Temp,args);




UPDATE:

I think I am on the right path here, however it seg faults right after it displays the results. Also my first attempt at recursion.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void Search(std::vector<Item>& db, std::vector<std::string>& args)
{
	std::cout << "\n";
	static std::vector<Item> temp;
	for(unsigned int i = args.size()-1; i > 0;)
	{
		for(unsigned int j = 0; j < db.size(); j++)
		{
			if(
				db[j].StockID.find(args[i]) != std::string::npos ||
				db[j].Type.find(args[i]) != std::string::npos ||
				db[j].Area.find(args[i]) != std::string::npos ||
				db[j].Serial.find(args[i]) != std::string::npos ||
				db[j].Date.find(args[i]) != std::string::npos ||
				db[j].Make.find(args[i]) != std::string::npos ||
				db[j].Model.find(args[i]) != std::string::npos ||
				db[j].ProcessorMake.find(args[i]) != std::string::npos ||
				db[j].ProcessorSpeed.find(args[i]) != std::string::npos ||
				db[j].MemoryMake.find(args[i]) != std::string::npos ||
				db[j].MemoryAmount.find(args[i]) != std::string::npos ||
				db[j].Quantity.find(args[i]) != std::string::npos ||
				db[j].Colour.find(args[i]) != std::string::npos ||
				db[j].Status.find(args[i]) != std::string::npos
			)
			{
				temp.push_back(db[i]);
				//std::cout << db[i];
				break;
			}
		}
		args.pop_back();
		if(args.size() > 0) Search(temp,args);
		else
		{
			DispHeader();
			for(unsigned int found = 0; found < temp.size(); found++) std::cout << temp[found];
			std::cout << "\n";
		}
	}
}



UPDATE 2:

This is my new solution, seems to work as is, feedback appreciated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void Search(std::vector<Item>& db, std::vector<std::string>& args)
{
	std::cout << "\n";
	std::vector<Item> temp;
	for(unsigned int j = 0; j < db.size(); j++)
	{
		if(
			db[j].StockID.find(args[args.size()-1]) != std::string::npos ||
			db[j].Type.find(args[args.size()-1]) != std::string::npos ||
			db[j].Area.find(args[args.size()-1]) != std::string::npos ||
			db[j].Serial.find(args[args.size()-1]) != std::string::npos ||
			db[j].Date.find(args[args.size()-1]) != std::string::npos ||
			db[j].Make.find(args[args.size()-1]) != std::string::npos ||
			db[j].Model.find(args[args.size()-1]) != std::string::npos ||
			db[j].ProcessorMake.find(args[args.size()-1]) != std::string::npos ||
			db[j].ProcessorSpeed.find(args[args.size()-1]) != std::string::npos ||
			db[j].MemoryMake.find(args[args.size()-1]) != std::string::npos ||
			db[j].MemoryAmount.find(args[args.size()-1]) != std::string::npos ||
			db[j].Quantity.find(args[args.size()-1]) != std::string::npos ||
			db[j].Colour.find(args[args.size()-1]) != std::string::npos ||
			db[j].Status.find(args[args.size()-1]) != std::string::npos
		)
		{
			temp.push_back(db[j]);
		}
	}
	args.pop_back();
	if(args.size() > 0) Search(temp,args);
	else
	{
		DispHeader();
		for(unsigned int found = 0; found < temp.size(); found++) std::cout << temp[found];
		std::cout << "\n";
		return;
	}
}
Last edited on
Something like this perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <string>
#include <vector>

struct item
{
    std::string StockID ;
    std::string Type ;
    std::string Make ;
    std::string Model ;
    // etc.

    bool has_attribute( const std::string& str ) const
    { return str == StockID || str == Type || str == Make || str == Model /* ... */ ; }

    bool has_all_attributes_in( const std::vector<std::string>& attrib_set ) const
    {
        for( const std::string& str : attrib_set ) if( !has_attribute(str) ) return false ;
        return true ;
    }

};

void search( const std::vector<item>& db, const std::vector<std::string>& attrib_set )
{
    for( const item& it : db ) if( it.has_all_attributes_in(attrib_set) )
        std::cout << it.StockID << ' ' << it.Type << ' ' << it.Make << ' ' << it.Model << '\n' ;
}

int main( int argc, char* argv[] )
{
    const std::vector<item> db
    {
        { "0001", "Laptop", "HP", "COMPAQ/2400T" },
        { "0002", "Laptop", "DELL", "INSPIRON/990" },
        { "0003", "Laptop", "Lenovo", "5431" },
        { "0002", "Desktop", "DELL", "whatever" },
        { "0002", "Laptop", "DELL", "some/other/model" },
        
        // ...
    } ;
    
    if( argc > 1 ) search( db, { argv+1, argv+argc } ) ;
}

http://coliru.stacked-crooked.com/a/b2ba0784306ec8c7
Last edited on
Hello again friend,

I had not considered using functions within the item data structure itself. It looks a little neater too.

I have a concern with this part of your code:

return str == StockID || str == Type || str == Make || str == Model

This would make it so the arguments have to be exact for anything to show up, this will cause a problem when people are inputting entries in different ways to others, for instance DELL, dell, Dell. I was going to look into a function I have seen before, something like to_lower() which will convert them all lower case for easier finding. Has your experience taught you any snippets like that?

I also used npos so the user could right something like:

Search INSP ^D


And all the Inspirons will come up.


Also on a side note, do you know a better way to stop the cin loop without the ^D?

1
2
3
4
5
6
7
8
9
10
else if(choice == "Search")
		{
			std::string arg = "";
			std::vector<std::string> args;
			while((std::cin >> arg) && arg != "^D")
			{
				args.push_back(arg);
			}
			Search(db,args);
		}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <cctype>
#include <string>
#include <sstream>

std::string to_lower( std::string str )
{
    for( char& c : str ) c = std::tolower(c) ;
    return str ;
}

std::string trim( std::string str ) // remove leading and trailing whitespace
{
    std::istringstream stm(str) ;
    stm >> str ;
    return str ;
}

std::string sanitise( std::string str ) { return to_lower( trim(str) ) ; }

bool begins_with( std::string str, std::string substr )
{ return to_lower(str).find( to_lower(substr) ) == 0 ; }

bool contains( std::string str, std::string substr )
{ return to_lower(str).find( to_lower(substr) ) != std::string::npos ; }

int main()
{
    // crude, but effective nevertheless:
    const std::string sentinel = "#stop" ;
    std::string str ;
    while( std::cin >> str && to_lower(str) != sentinel ) { /* .. */ }
}


Regular expressions: http://en.cppreference.com/w/cpp/regex
String algorithms: http://www.boost.org/doc/libs/1_57_0/doc/html/string_algo.html
Last edited on
Those string functions a great thanks for those,

The thing I want to mention is that the "sentinel" as you called it, still needs to be inputted, I found this nifty piece of code:

1
2
3
4
while((std::cin.peek() == ' ') && std::cin >> arg) // Checks if another sequence is in the stream
{
	args.push_back(arg);
}


I am guessing the inputted delimiter is more robust an apparent, I just hate the idea of having to input it (I often forget).


Also, slightly on topic, kinda off, could you check this function I made using what you taught me previously? Sorry, I have so much on my mind at the moment.

1
2
3
4
5
6
7
8
9
10
11
12
13
template<typename arg>
void print(arg a)
{
	std::cout << a;
	std::cout.flush();
}

template<typename arg, typename ...ARGS>
void print(arg a, ARGS&&... args)
{
	print(a);
	print((args)...);
}


It took a bit of time getting it to work, I kept getting "expected ; before ..." so I had to do some hacking around. It seems to work okay, it just doesn't look natural to me.
Looks ok.

You might want to pass the parameter by reference to const,
and avoid gratuitous flushing of the stream after every output.

1
2
3
4
5
6
template<typename arg>
void print( const arg& a )
{
	std::cout << a;
	//std::cout.flush();
}
Do you know if I have to define the same function twice? Can't I just have the one with a templated parameter pack?

Also does flushing cause efficiency problems? I have been doing so work with threads today, I had to use flush so output come out while the threads were running.
> Can't I just have the one with a templated parameter pack?

The parameters pack contains a variable number of parameters;
recursively unpacking them and processing them one by one is canonical.


> Also does flushing cause efficiency problems?

Yes, flushing after every output is somewhat inefficient.
Writing '\n' to stdout would trigger an automatic flush anyway.
Assuming that there was no prior std::cout.sync_with_stdio(false)


> I have been doing so work with threads today, I had to use flush so output come out while the threads were running.

Yes. That is a typical scenario where flush would be used.

See: http://en.cppreference.com/w/cpp/io/manip/flush
Last edited on
Thank you my man, you've been a great help.
Topic archived. No new replies allowed.