Creating new List dynamically from successive elements of another list - java

I am working on an issue where i have an input integer list such as {0,3,6,9,12,18,21,24,27,33,39}. I need to create a new list by analyzing the original list such that if i find a sequence of multiple of 3, then first element and last element of that sequence will both be added to that list. None of the elements between the first and last element of that sequence will be added. Any other elements (without consecutive multiples of 3) will be added as such. So for an input list of {0,3,6,9,12,18,24,27,30,39,45}, my output should be [0,12,18,24,30,39,45]. Note that my first sequence of multiples of 3s was 0,3,6,9,12; so based on my requirements, the new list has 0, 12. Since 18 does not have any neighbor that differs by 3 on either side, it is added as such. Similarly, 24,27,30 are added as 24,30. Also, similar to 18, 39 and 45 are added as such. Below is my code snippet:
int difference = 3;
public static List<Integer> parseList(List<Integer> input, int difference) {
List<Integer> output = new ArrayList<Integer> ();
for (int i = 0; i < input.size()-1; i++) {
// Check if subsequent element values differ by specific difference
if ( (input.get(i+1) - input.get(i)) == difference) {
output.add(input.get(i));
output.add(input.get(i+1));
}
else {
output.add(input.get(i));
}
}
return output;
}
My resulting output is: 0 3 3 6 6 9 9 12 12 18 24 27 27 30 30 39
Again, my expected output is: [0,12,18,24,30,39,45]
Note that not only i am getting intermediate duplicate values, i am also missing last value (i.e.45).

As mentioned in another post, you do not get the last element of the list because your for loop terminates on the next to last element of the input list. However, if you just adjust the end condition of your for loop, you will eventually get an IndexOutOfBoundsException because your algorithm checks for i+1 on each loop.
I think it is simpler to start at index 1 and simply look backward in the array instead. From your description of the problem, I understand that you will always use the first element of the list, so we can insert it right from the start (but that means you need to make sure the input list contains at least one element):
public static List<Integer> parseList(List<Integer> input, int difference) {
List<Integer> output = new ArrayList<Integer> ();
if (input.size() > 0) {
// always use first element
int indexToAdd = -1;
output.add(input.get(0));
for (int i = 1; i < input.size(); i++) {
if ( (input.get(i) - input.get(i-1)) == difference) {
if (i == input.size()-1) {
output.add(input.get(i));
}
else {
indexToAdd = i;
}
}
else {
if(indexToAdd != -1) {
output.add(input.get(indexToAdd));
indexToAdd = -1;
}
output.add(input.get(i));
}
}
}
return output;
}

for (int i = 0; i < input.size()-1; i++)
This causes you to miss the last element in the list.
Therefore you should add the last element after the loop (since the last element should always appear in the output).
The following:
if ( (input.get(i+1) - input.get(i)) == difference) {
output.add(input.get(i));
output.add(input.get(i+1));
causes the duplicate entries, since whenever you encounter a couple of elements that differ by 3, you enter both of them.
Instead, when you discover that the difference is three, you should set some flag to true, and set it to false otherwise. As long as the flag is true, you don't put elements in the output list.
I believe something like this will do the trick:
public static List<Integer> parseList(List<Integer> input, int difference) {
List<Integer> output = new ArrayList<Integer> ();
boolean isSeq = false;
for (int i = 0; i < input.size()-1; i++) {
if ((input.get(i+1) - input.get(i)) == difference) {
// add start of a sequence
if (!isSeq) {
output.add(input.get(i));
isSeq = true;
}
}
else {
isSeq = false;
output.add(input.get(i));
}
}
output.add(input.get(input.size()-1));
return output;
}

The method parseList in your code is incorrect. Two issues are in this method.
1.It will add the numbers as long as adjacent numbers differ by 3. In this situation you will add duplicate numbers when consecutive differ occurs.
For example, 0,3,6,9,12
Step#1. when i =0, condition 'adjacent numbers differ by 3 ' satisfiies, you add 0 and 3 to the output list.
step#2 when i =1, condition 'adjacent numbers differ by 3 ' satisfiies, yop add 3 and 6 to the output list, see what? duplicate values are added. for the index i=1, you add the number to output list both in step#1 and step#2. That is why you got duplicate numbers in your output list, such as 0,3,3,6....
2. Another issue in your parseList method is that the last number is not processed, it is ignored when the last 2 numbers not differs by 3. That is why you are not able to see it in your output list either.
You can add variable to check if consecutive number differ occurs and add logic to handle the last number as well.
The following code example can help you out with this problem.

It took me a while to figure it out.... Yet, another way to code it:
ArrayList<Integer> output = new ArrayList<Integer>();
int i = 0;
while (i < input.size()) {
int start = input.get(i);
output.add(start);
int end = -1;
int x = i+1;
while (x < input.size()) {
if (input.get(x++) - input.get(i++) != 3) {
end = input.get(i-1);
break;
}
}
if (end > 0 && end != start) {
output.add(end);
}
if (start == input.get(i)) break;
}
return output;

Related

Java beginner q - iterate ArrayList, select+remove items problem [duplicate]

This question already has answers here:
ArrayList.remove is not working in a loop
(7 answers)
Closed 2 years ago.
I'm trying to remove even-length words from a String ArrayList, and it's almost working, except that for some reason one even-numbered word is getting through.
My code:
public ArrayList<String> removeEvenLength(ArrayList<String> a) {
for (int i = 0; i < a.size(); i++) {
String wordEntry = a.get(i);
if (wordEntry.length() % 2 == 0) {
a.remove(i);
}
}
return a;
I must be missing something, but I'm failing to determine what exactly. Pointers much appreciated.
The issue is that you are modifying the ArrayList while iterating over it, which changes its size. You need to decrease the index by one each time you remove an element since that index will now refer to the next element.
public static ArrayList < String > removeEvenLength(ArrayList < String > a) {
for (int i = 0; i < a.size(); i++) {
String wordEntry = a.get(i);
if (wordEntry.length() % 2 == 0) {
a.remove(i);
i--;
}
}
return a;
}
Looping backwards will also fix this problem, as elements will never be shifted to a position that you have yet to check.
public static ArrayList < String > removeEvenLength(ArrayList < String > a) {
for (int i = a.size() - 1; i >= 0; i--) {
String wordEntry = a.get(i);
if (wordEntry.length() % 2 == 0) {
a.remove(i);
}
}
return a;
}
You can also use List#removeIf with Java 8 and up to accomplish this easier.
public static ArrayList < String > removeEvenLength(ArrayList < String > a) {
a.removeIf(str -> str.length() % 2 == 0);//or str.length() & 1 == 0
return a;
}
You can use Stream#filter to construct a new List with the odd-length Strings without modifying the old one.
public static ArrayList < String > removeEvenLength(ArrayList < String > a) {
return a.stream().filter(str -> str.length() % 2 == 1).collect(Collectors.toCollection(ArrayList::new));
}
Your code is working fine but as you will delete any element from a particular index then the list index will change means the immediate element after the removed element will be updated to removed element.
You can modify your code to below code:
public ArrayList<String> removeEvenLength(ArrayList<String> a) {
for (int i = 0; i < a.size(); i++) {
String wordEntry = a.get(i);
if (wordEntry.length() % 2 == 0) {
a.remove(i);
i-=1;
}
}
return a;
}
The issue here is that when you do a.remove(i);, the ArrayList automatically updates its indices, so you end up skipping a value. Here's an example of how this could happen:
You get the 1st element (a.get(0);)
You find that it is an even length string (wordEntry.length() % 2 == 0)
You remove it (a.remove(i);)
Your for loop advances to the next value (now i = 1)
You get what is now the 2nd element (a.get(1);), but because you removed what was the 1st element, this is now what used to be the 3rd element.
In this scenario, you skipped over that 2nd element, which is why some strings are slipping through unnoticed. This is where I would suggest using the enhanced for loop, which simplifies things significantly so you don't need to worry about what index you are on. It would look something like this:
public ArrayList<String> removeEvenLength(ArrayList<String> a) {
for (String wordEntry : a) {
if (wordEntry.length() % 2 == 0) {
a.remove(wordEntry);
}
}
return a;
}
Alternatively, if you want to go for an even simpler one-liner, ArrayList offers some very convenient methods for manipulating ArrayLists. One such method, called removeIf(), pretty much does exactly what you want, but I think it's good to learn to properly use for loops before going on to use built-in methods like this. That said, if you wanted to go that route, here's how I would do it:
public ArrayList<String> removeEvenLength(ArrayList<String> a) {
a.removeIf((wordEntry) -> wordEntry.length() % 2 == 0);
return a;
}
Also, I just felt that I should note that there are other ways of finding even numbers. You can also use (wordEntry.length() & 1) == 0. There's no real difference as far as performance or anything, it's really just personal preference, but I just felt I should mention another way of doing it :)
Your method gets out of sync with the elements when removing from front to back. So remove them in reverse order.
ArrayList<String> words = new ArrayList<>(List.of("abc", "efgh", "o", "pq", "rs"));
words = removeEvenLength(words);
System.out.println(words);
public static ArrayList<String> removeEvenLength(ArrayList<String> a) {
for (int i = a.size()-1; i >= 0; i--) {
String wordEntry = a.get(i);
if (wordEntry.length() % 2 == 0) {
a.remove(i);
}
}
return a;
}
And as stated you can also use removeIf()
Explanation.
As you move forward, removing elements, your index is still incrementing normally to get to the next element. But the list has changed by removing previous elements so the index may skip over elements that need to be checked.
Assume you want to remove even elements.
consider a = [5,20,40], index = 1
remove a[index++]; the list is now [5,40] and index = 2. 40 will not be checked because the list is now of size 2 and the iteration will cease.
By removing them in reverse, the decrease in the length of the list does not impact the index.
again consider a = [5,20,40], index = 2
remove a[index--]; the list is now [5,20] and index = 1. 20 will be checked and removed. Index will then be 0 and one element will remain.
This behavior can be mitigated by adjusting the index when removing items. However, by removing in reverse order, no such adjustment is required.
Remove while iterating makes the problem. You can use removeIf for this
a.removeIf(wordEntry -> (wordEntry.length() % 2 == 0));
or use ListIterator to iterate the arraylist
ListIterator<String> iter = a.listIterator();
while(iter.hasNext()){
if(iter.next().length() % 2 == 0){
iter.remove();
}
}

Remove two or more consecutive numbers ending in 6 in the array

I have tried it but it is working only for two consecutive digits and not for more than two. Please find the code below which I've tried and kindly suggest a solution for it.
Sample Input:
3
6
36
62
121
66
26
376
661
6
Sample Output:
3
62
121
661
6
My code:
public static List<Integer> processArray(ArrayList<Integer> array) {
int num=0;
int prevnum=0;
for(int i =0;i<array.size();i++) {
num=array.get(i);
if(i>0 && array.get(i-1)%10==6 && num%10==6) {
array.remove(i-1);
array.remove(i-1);
}
}
return array;
}
As mentioned in the comments. The first solution just happens to work for your current use case. For a more flexible solution you can use the following snippet:
public static List<Integer> processList(List<Integer> list){
int encountered = 0;
for(ListIterator<Integer> it = list.listIterator(); it.hasNext(); ){
int i = it.next();
if(i % 10 == 6){
encountered++;
} else {
encountered = 0;
}
if(encountered > 1){
it.remove();
if(encountered == 2){
it.previous();
it.remove();
}
}
}
return list;
}
It uses a listIterator and a counter variable. That way we can traverse the list, count how many 6 there are and if its more than 2 in a row then remove them.
When not using an iterator you begin to skip elements once you start removing some. Because when removing an element at an index, the List is shortened. Hence the element 1 index above will slide into the missing spot. When you then increment the index in the next step you'll have skipped an element.
OLD
I would suggest using an iterator and 2 for loops to achieve that:
public static List<Integer> processList(List<Integer> list){
for(Iterator<Integer> it = list.iterator(); it.hasNext(); ){
for(int i = it.next(); i % 10 == 6 && it.hasNext(); i = it.next()){
it.remove();
}
}
return list;
}
The first for loop iterates the list as usual, where as the second one will remove the numbers if the last digit is a 6.
When deleting elements from a list I always find it simplest to process the list backward, from the end towards the beginning. In this way the only indices that shift when deleting elements are the ones that I am already done with, so I can ignore the shifting.
public static void processArray(ArrayList<Integer> array) {
// process from end of list backward
int ix = array.size();
while (ix > 0) {
if (array.get(ix - 1) % 10 == 6) {
// find beginning of run of numbers that end in 6
int beginIx = ix - 1;
while (beginIx > 0 && array.get(beginIx - 1) % 10 == 6) {
beginIx--;
}
List<Integer> runOf6 = array.subList(beginIx, ix);
if (runOf6.size() >= 2) {
// delete entire run
runOf6.clear();
}
ix = beginIx;
}
else {
ix--;
}
}
}
Demo:
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(3, 6, 36, 62, 121, 66, 26, 376, 661, 6));
processArray(list);
System.out.println(list);
Output:
[3, 62, 121, 661, 6]
Edit: Another option is to go the opposite way: to create the result list by adding those elements from the original list that are not part of a run of at least two numbers ending in 6. As a special case, if the original list has length 0 or 1, return it as is since there just can’t be two number ending in 6 in it. Otherwise create a result list. Iterate forward through the original list. For each element, if it ends in 6 and also has a neighbour on (at least) one side that ends in 6, skip it; otherwise add it to the result. Take care not to check the left neighbour of the first element or the right neighbour of the last one since they don’t exist and you will get an IndexOutOfBoundsException. I am leaving it to you to explore this option if you want to.
There is a small problem within the for loop. As your for loop iterates through Arraylist, the value of i increments meanwhile the size your Arraylist decrease because of you're removing the elements ending with 6 .

Remove Leading zeroes from an ArrayList of integers

I have an ArrayList of integers, I want to remove all the leading zeroes, code seems all right but U am getting unusual output.
Input:
0 0 0 1 9 9
Output:
0 1 9 9
Expected output:
1 9 9
public class Solution {
public ArrayList<Integer> plusOne(ArrayList<Integer> a) {
int flag=0;
//System.out.println(a.size()+" "+a.get(2));
for(int i=0;i<a.size();i++)
{
if(flag==0)
{
//System.out.println("val of i="+i+" "+a.get(i));
if(a.get(i)==0){
a.remove(i);
//System.out.println(flag);
}
else
{
//System.out.println("flag="+flag+" i="+i+" value"+a.get(i));
flag=1;
//System.out.println("flag="+flag+" i="+i+" value"+a.get(i));
}
}
if(flag==1)
break;
}
System.out.println();
return a;
}
}
You can remove the leading zeros by just searching for the first non-zero value, and then clearing the preceding sublist:
Iterator<Integer> it = list.iterator();
int i = 0;
while (it.hasNext() && it.next() == 0) {
++i;
}
list.subList(0, i).clear();
Removing a block of the list like this can be more efficient than removing the elements one at a time. e.g. if you removed them one at a time, ArrayList would shift all of the tail elements one position along each time, so the removal would be O(n^2).
The problem is that you're removing elements from the list while you're iterating over it. When i = 0:
a.remove(i);
removes the first element of the list and all elements are shifted: the 2nd becomes the 1st, etc. Then in the for loop, i is set to 1 after that. Hence, the second element is ignored: it became the first after the remove operation and i jumped over it because it was incremented.
The ugly solution would be to have i--; right after a.remove(i); to account for that shift.
However, a better solution would be to use a ListIterator:
public ArrayList<Integer> plusOne(ArrayList<Integer> a) {
ListIterator<Integer> it = a.listIterator();
while (it.hasNext() && it.next() == 0) {
it.remove();
}
return a;
}
This code retrieves it with listIterator(). While there are still elements and the next element is 0, we remove it with remove().
The problem is that i is incrementing while a.size() is shrinking. When i==0 you remove element 0 so all the values shift down 1, and next you remove element 1 but element 0 is also 0 so you skip this. i.e. you are only removing half the leading zeros.
BTW You should be able to confirm this by stepping through your code in your debugger. Helping you understand your code and find bugs is what it is for.
The simplest change is
for (int i = 0, max = a.size(); i < max; i++)
and
// you only want to check the first element.
if (a.get(0) == 0)
a.remove(0);
A more efficient way of doing this is to find the first element which is not 0 and return a sub list
public static List<Integer> trimLeadingZeros(List<Integer> list) {
for (int i = 0; i < list.size(); i++)
if (list.get(i) != 0)
return list.subList(i, list.size());
return Collections.emptyList();
}
Logic: anytime you see 0, loop from that point to the end, if inside that loop you see anything other than 0, break out from that loop.
Code:
//a is arrayList;
int count = 0;
for(int c = b; c < size; c++){
if(a.get(c) == 0 )
{
count++;
}
}
//size = 10,saw zero at 6th position that means leading zeros has to be 4,
// b is when I first saw 0
if(count == (size -b)) {}
else {
//else we just copy
ret.add(b, a.get(b));
}
Loop through the ArrayList and if you encounter 0 just remove it.
while (i < a.size() - 1 && a.get(i) == 0) {
a.remove(i);
}
Best Solution:
while(a.get(0)==0) { a.remove(0); }

Removing elements in ArrayList with certain size

So I'm currently stuck on one of my programs. I have to use a for loop to remove all elements in the ArrayList that's greater than 4. I have no idea how to do this. Someone help?
Here is my code:
import java.util.ArrayList;
public class P4E
{
public static void main( String args[] )
{
ArrayList<String> universities = new ArrayList<String>();
universities.add("Princeton");
universities.add("UCSD");
universities.add("UCLA");
universities.add("SDSU");
universities.add("UCI");
int size = universities.size();
System.out.println( "The size of the ArrayList is: " + size);
System.out.println("");
System.out.println("Now using a for-each loop");
System.out.println("");
for (String a : universities)
{
System.out.println( a );
}//end for-each
System.out.println( "" );
System.out.println( "Now using a for loop" );
System.out.println( "" );
for (int i = 0; i < universities.size(); i++)
{
if (universities.get(i) > 4 )
{
universities.remove(i);
}//end if-statement
}//end for loop
}//end main
}
Use the Java 8 removeIf default method on List:
universities.removeIf(u -> u.length() > 4);
If you're trying to remove elements that are larger than 4 characters, this will work:
for (int i = 0; i < universities.size(); i++)
{
System.out.println("checking "+universities.get(i));
if (universities.get(i).length() > 4 )
{
universities.remove(i);
i--;
}//end if-statement
}//end for loop
The .length() is necessary because you're interested in the size of the string, you can't compare a string to an int directly. The i--; ensures that you don't skip over elements as the size of the list changes.
The System.out.println is to show you what elements it is checking, so you can see that you're walking through the list correctly.
Instead of if(universities.get(i) > 4)
Do this:
for (int i = size; i >= 0; i--)
{
if (i > 4 )
universities.remove(i); // Shorten to only 1 line instead of brackets
}//end for loop
Reason
You want to remove the university at point i in this case, not get the university object. When you call universities.get(i) it returns the Object in the universities ArrayList
With current code problem is in the fact that you increasing i every time, while sometimes you remove element which cause indexes for elements to the left to be shifted. This results in the fact that you skip check for some elements. Example:
Initial array: [5,6,7], index i = 0.
You removed element at index 0 - array became [6,7], then you increase i and it became 1 - so index for next element to check is 1 and this reffer to 7, not to 6. Result - you didn't check 6.
Solutions:
1) Add i--; immediately after universities.remove(i);
2) Iterate in descending order: for (int i = 0; i < universities.size(); i++) -> for (int i = universities.size() - 1; i >= 0; i--)
3) Use java streams:
universities = universities.stream()
.filter(e -> e > 4)
.collect(Collectors.toList());
The problem with your code is that i advances to the next value even after a remove() call, meaning that your code will skip some items.
Here is what's happening: let's say you want to delete all numbers 4 and above from this list: {9 5 7 4 3}. When i is zero, your algorithm will find 9 and remove it. Now i is 1, but your list is {5 7 4 3}, so 5 at index zero will never be checked again.
One solution is to iterate in reverse order:
for (int i = universities.size() - 1; i >= 0 ; i--) {
if (universities.get(i).length() > 4 ) {
universities.remove(i);
}//end if-statement
}//end for loop
Now that i goes down instead of going up it does not matter whether the list shrinks or not.
What exactly do you mean by greater than 4? If you meant length of the String greater than 4, then here is the solution. Replace the 2nd for loop with the code below.
int i = 0;
while(i<universities.size()) {
if (universities.get(i).length() > 4 ) {
universities.remove(i);
} else {
i++;
}
}

searching multiple number in given array

have following problem. suppose i have an array number[n] , i want search multiple number , for example i want to search 12, 45 ,1 ,6,8,5, and if every number present array then i can get favorable result. so there is one way , i just pick one element like 7
if it is present in array number[n], then can get inside the loop , and again initialize another loop and check that if second number is in the number[n] , and so on, so here i need same number of loop as the number of searching numbers. so is there is another way to deal with such problem. because it will running time will be polynomial.
here is my code:
import java.util.Scanner;
class Number {
boolean check(int[] num)
{
for (int i = 0; i < 5; i++) {
if (num[i] == 7) {
for (j = 0; j < 5; j++) {
if (num[j] == 8) {
for (int k = 0; k < 5; k++) {
if (num[k] == 9) {
return true;
}
else
continue;
}
} else
continue;
}
} else
return false;
}
}
public static void main(string [] args)
{
Number obj1 = new Number();
Scanner input = new Scanner(System.in);
int [] num =new int[5];
for(int i=0;i<5;i++)
num[i] =input.nextInt();
boolean get ;
get = obj1.check(num []);
System.out.print(response);
}
}
You could do something like this.
public static boolean allFoundIn( int[] toSearch, int... numbers )
Set numbersSet = new HashSet(Arrays.asList(numbers));
numbersSet.removeAll(Arrays.asList(toSearch));
return numbersSet.isEmpty();
}
Then in your main, just call
allFoundIn(num, 7, 8, 9);
which will return true if 7, 8 and 9 are all found in the array num.
If you want a sub-polynomial solution then there are a few possibilities.
1) Sort both lists, then loop like so (pseudocode)
toFind = <first element of listToFind>
for i in listToSearch:
if i == toFind:
if toFind is last element of listToFind:
return true
toFind = next element of listToFind
else if i > toFind:
return false
2) Put all the elements of the list to search in a HashSet. Then loop over the elements you want to find and see if it's in the HashSet. If they all are then they're all in the list. If not then they're not all in the list. HashSet has fast lookup, so it will likely be better than polynomial time.
and since I was already beaten to the punch for 2, I'll stop thinking of alternatives and post.
Yes, you can dramatically reduce the number of passes. Firstly though, don't hard code you search numbers like that with a separate loop for each. Create one array to store the numbers being searched for and one containing the numbers being searched. Sort each in the same direction, eg ascending order. Create two ints to act as counters, one for each array. Now use a while loop to compare the numbers in each array at the positions the counters are at.
How you advance the counters depends on how the numbers compare. If the number in the array of ones being searched for is larger than the one being searched, you advance the one being searched. If the other way around you advance the one being searched and if equal you advance both and record the match. Keep going until the end of one array is reached.
Using this method you only traverse the arrays a maximum of one time. I'd write example code but I'm typing on my phone!
This solution is not the fastest since it does a binary search for every number. Additionally, it has to be sorted first. It would be better to put all your source numbers into a hash set, like in David Wallace's solution. Then each search time is constant instead of depending on the size of your source array.
boolean check(int[] num) {
int[] toSearch = new int[] { 12, 45, 1, 6, 8, 5 };
for (int search : toSearch) {
if (Arrays.binarySearch(num, search) == -1) {
return false;
}
}
return true;
}
If you want to use a hash set, you could do it like this:
boolean check(Integer[] num) {
HashSet<Integer> numSet = new HashSet<>(Arrays.asList(num));
int[] toSearch = new int[] { 12, 45, 1, 6, 8, 5 };
for (int search : toSearch) {
if (!numSet.Contains(search)) {
return false;
}
}
return true;
}

Categories

Resources