Categories
discuss

Perform operation on n random distinct elements from Collection using Streams API

I’m attempting to retrieve n unique random elements for further processing from a Collection using the Streams API in Java 8, however, without much or any luck.

More precisely I’d want something like this:

Set<Integer> subList = new HashSet<>();
Queue<Integer> collection = new PriorityQueue<>();
collection.addAll(Arrays.asList(1,2,3,4,5,6,7,8,9));
Random random = new Random();
int n = 4;
while (subList.size() < n) {
  subList.add(collection.get(random.nextInt()));
}
sublist.forEach(v -> v.doSomethingFancy());

I want to do it as efficiently as possible.

Can this be done?

edit: My second attempt — although not exactly what I was aiming for:

List<Integer> sublist = new ArrayList<>(collection);
Collections.shuffle(sublist);
sublist.stream().limit(n).forEach(v -> v.doSomethingFancy());

edit: Third attempt (inspired by Holger), which will remove a lot of the overhead of shuffle if coll.size() is huge and n is small:

int n = // unique element count
List<Integer> sublist = new ArrayList<>(collection);   
Random r = new Random();
for(int i = 0; i < n; i++)
    Collections.swap(sublist, i, i + r.nextInt(source.size() - i));
sublist.stream().limit(n).forEach(v -> v.doSomethingFancy());

Answer

The shuffling approach works reasonably well, as suggested by fge in a comment and by ZouZou in another answer. Here’s a generified version of the shuffling approach:

static <E> List<E> shuffleSelectN(Collection<? extends E> coll, int n) {
    assert n <= coll.size();
    List<E> list = new ArrayList<>(coll);
    Collections.shuffle(list);
    return list.subList(0, n);
}

I’ll note that using subList is preferable to getting a stream and then calling limit(n), as shown in some other answers, because the resulting stream has a known size and can be split more efficiently.

The shuffling approach has a couple disadvantages. It needs to copy out all the elements, and then it needs to shuffle all the elements. This can be quite expensive if the total number of elements is large and the number of elements to be chosen is small.

An approach suggested by the OP and by a couple other answers is to choose elements at random, while rejecting duplicates, until the desired number of unique elements has been chosen. This works well if the number of elements to choose is small relative to the total, but as the number to choose rises, this slows down quite a bit because of the likelihood of choosing duplicates rises as well.

Wouldn’t it be nice if there were a way to make a single pass over the space of input elements and choose exactly the number wanted, with the choices made uniformly at random? It turns out that there is, and as usual, the answer can be found in Knuth. See TAOCP Vol 2, sec 3.4.2, Random Sampling and Shuffling, Algorithm S.

Briefly, the algorithm is to visit each element and decide whether to choose it based on the number of elements visited and the number of elements chosen. In Knuth’s notation, suppose you have N elements and you want to choose n of them at random. The next element should be chosen with probability

(n – m) / (N – t)

where t is the number of elements visited so far, and m is the number of elements chosen so far.

It’s not at all obvious that this will give a uniform distribution of chosen elements, but apparently it does. The proof is left as an exercise to the reader; see Exercise 3 of this section.

Given this algorithm, it’s pretty straightforward to implement it in “conventional” Java by looping over the collection and adding to the result list based on the random test. The OP asked about using streams, so here’s a shot at that.

Algorithm S doesn’t lend itself obviously to Java stream operations. It’s described entirely sequentially, and the decision about whether to select the current element depends on a random decision plus state derived from all previous decisions. That might make it seem inherently sequential, but I’ve been wrong about that before. I’ll just say that it’s not immediately obvious how to make this algorithm run in parallel.

There is a way to adapt this algorithm to streams, though. What we need is a stateful predicate. This predicate will return a random result based on a probability determined by the current state, and the state will be updated — yes, mutated — based on this random result. This seems hard to run in parallel, but at least it’s easy to make thread-safe in case it’s run from a parallel stream: just make it synchronized. It’ll degrade to running sequentially if the stream is parallel, though.

The implementation is pretty straightforward. Knuth’s description uses random numbers between 0 and 1, but the Java Random class lets us choose a random integer within a half-open interval. Thus all we need to do is keep counters of how many elements are left to visit and how many are left to choose, et voila:

/**
 * A stateful predicate that, given a total number
 * of items and the number to choose, will return 'true'
 * the chosen number of times distributed randomly
 * across the total number of calls to its test() method.
 */
static class Selector implements Predicate<Object> {
    int total;  // total number items remaining
    int remain; // number of items remaining to select
    Random random = new Random();

    Selector(int total, int remain) {
        this.total = total;
        this.remain = remain;
    }

    @Override
    public synchronized boolean test(Object o) {
        assert total > 0;
        if (random.nextInt(total--) < remain) {
            remain--;
            return true;
        } else {
            return false;
        }
    }
}

Now that we have our predicate, it’s easy to use in a stream:

static <E> List<E> randomSelectN(Collection<? extends E> coll, int n) {
    assert n <= coll.size();
    return coll.stream()
        .filter(new Selector(coll.size(), n))
        .collect(toList());
}

An alternative also mentioned in the same section of Knuth suggests choosing an element at random with a constant probability of n / N. This is useful if you don’t need to choose exactly n elements. It’ll choose n elements on average, but of course there will be some variation. If this is acceptable, the stateful predicate becomes much simpler. Instead of writing a whole class, we can simply create the random state and capture it from a local variable:

/**
 * Returns a predicate that evaluates to true with a probability
 * of toChoose/total.
 */
static Predicate<Object> randomPredicate(int total, int toChoose) {
    Random random = new Random();
    return obj -> random.nextInt(total) < toChoose;
}

To use this, replace the filter line in the stream pipeline above with

        .filter(randomPredicate(coll.size(), n))

Finally, for comparison purposes, here’s an implementation of the selection algorithm written using conventional Java, that is, using a for-loop and adding to a collection:

static <E> List<E> conventionalSelectN(Collection<? extends E> coll, int remain) {
    assert remain <= coll.size();
    int total = coll.size();
    List<E> result = new ArrayList<>(remain);
    Random random = new Random();

    for (E e : coll) {
        if (random.nextInt(total--) < remain) {
            remain--;
            result.add(e);
        }
    }            

    return result;
}

This is quite straightforward, and there’s nothing really wrong with this. It’s simpler and more self-contained than the stream approach. Still, the streams approach illustrates some interesting techniques that might be useful in other contexts.


Reference:

Knuth, Donald E. The Art of Computer Programming: Volume 2, Seminumerical Algorithms, 2nd edition. Copyright 1981, 1969 Addison-Wesley.

Categories
discuss

How to add floating action button on top of scrollview

I have a floating action button that I would like to add on top of a scrollview so that the button stays, even if you scroll. I want it on the top of the scrollview and in the bottom right corner of the screen. What combination of views do I need to use to accomplish this?

Here is the xml file:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:fab="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.nhscoding.safe2tell.STORIES"
    android:background="@color/stor_back">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="bottom"
        >

        <com.getbase.floatingactionbutton.FloatingActionButton
            android:id="@+id/pink_icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            fab:fab_icon="@drawable/ic_add"
            fab:fab_colorNormal="@color/fab_back"
            fab:fab_colorPressed="@color/fab_pressed_back"
            android:layout_gravity="end"
            android:layout_marginBottom="16dp"
            android:layout_marginRight="16dp"/>
        </LinearLayout>


    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">



            <android.support.v7.widget.CardView
                xmlns:card_view="http://schemas.android.com/apk/res-auto"
                android:id="@+id/card_view1"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_marginTop="@dimen/card_margin_top"
                android:layout_marginBottom="@dimen/card_margin_bottom"
                android:layout_marginLeft="@dimen/card_margin_left"
                android:layout_marginRight="@dimen/card_margin_right"
                card_view:cardCornerRadius="@dimen/card_radius"
                >

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/card_title_top"
                    android:layout_marginBottom="@dimen/card_title_bottom"
                    android:layout_marginLeft="@dimen/card_title_left"
                    android:layout_marginRight="@dimen/card_title_right"
                    android:text="@string/card1_title"
                    android:textSize="@dimen/card_title_size"
                    android:gravity="top"
                    android:id="@+id/title1"
                    />


            </android.support.v7.widget.CardView>

            <android.support.v7.widget.CardView
                xmlns:card_view="http://schemas.android.com/apk/res-auto"
                android:id="@+id/card_view2"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_marginTop="@dimen/card_margin_top"
                android:layout_marginBottom="@dimen/card_margin_bottom"
                android:layout_marginLeft="@dimen/card_margin_left"
                android:layout_marginRight="@dimen/card_margin_right"
                card_view:cardCornerRadius="@dimen/card_radius"
                android:layout_below="@id/card_view1">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/card_title_top"
                    android:layout_marginBottom="@dimen/card_title_bottom"
                    android:layout_marginLeft="@dimen/card_title_left"
                    android:layout_marginRight="@dimen/card_title_right"
                    android:text="@string/card2_title"
                    android:textSize="@dimen/card_title_size"
                    android:gravity="top"
                    />

                <TextView
                    android:id="@+id/info_text2"
                    android:layout_marginTop="16dp"
                    android:layout_marginBottom="0dp"
                    android:layout_marginLeft="16dp"
                    android:layout_marginRight="16dp"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
            </android.support.v7.widget.CardView>

            <android.support.v7.widget.CardView
                xmlns:card_view="http://schemas.android.com/apk/res-auto"
                android:id="@+id/card_view3"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_marginTop="@dimen/card_margin_top"
                android:layout_marginBottom="@dimen/card_margin_bottom"
                android:layout_marginLeft="@dimen/card_margin_left"
                android:layout_marginRight="@dimen/card_margin_right"
                card_view:cardCornerRadius="@dimen/card_radius"
                android:layout_below="@id/card_view2">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/card_title_top"
                    android:layout_marginBottom="@dimen/card_title_bottom"
                    android:layout_marginLeft="@dimen/card_title_left"
                    android:layout_marginRight="@dimen/card_title_right"
                    android:text="@string/card3_title"
                    android:textSize="@dimen/card_title_size"
                    android:gravity="top"
                    />

                <TextView
                    android:id="@+id/info_text3"
                    android:layout_marginTop="16dp"
                    android:layout_marginBottom="0dp"
                    android:layout_marginLeft="16dp"
                    android:layout_marginRight="16dp"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"/>
            </android.support.v7.widget.CardView>

            <android.support.v7.widget.CardView
                xmlns:card_view="http://schemas.android.com/apk/res-auto"
                android:id="@+id/card_view1"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_marginTop="@dimen/card_margin_top"
                android:layout_marginBottom="@dimen/card_margin_bottom"
                android:layout_marginLeft="@dimen/card_margin_left"
                android:layout_marginRight="@dimen/card_margin_right"
                card_view:cardCornerRadius="@dimen/card_radius">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/card_title_top"
                    android:layout_marginBottom="@dimen/card_title_bottom"
                    android:layout_marginLeft="@dimen/card_title_left"
                    android:layout_marginRight="@dimen/card_title_right"
                    android:text="@string/card4_title"
                    android:textSize="@dimen/card_title_size"
                    android:gravity="top"
                    android:id="@+id/title4"
                    />


            </android.support.v7.widget.CardView>

            <android.support.v7.widget.CardView
                xmlns:card_view="http://schemas.android.com/apk/res-auto"
                android:id="@+id/card_view1"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_marginTop="@dimen/card_margin_top"
                android:layout_marginBottom="16dp"
                android:layout_marginLeft="@dimen/card_margin_left"
                android:layout_marginRight="@dimen/card_margin_right"
                card_view:cardCornerRadius="@dimen/card_radius">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/card_title_top"
                    android:layout_marginBottom="@dimen/card_title_bottom"
                    android:layout_marginLeft="@dimen/card_title_left"
                    android:layout_marginRight="@dimen/card_title_right"
                    android:text="@string/card5_title"
                    android:textSize="@dimen/card_title_size"
                    android:gravity="top"
                    android:id="@+id/title5"
                    />


            </android.support.v7.widget.CardView>


            </LinearLayout>
        </ScrollView>




</RelativeLayout>

Answer

Later children in a RelativeLayout tend to float over earlier children in a RelativeLayout.

(I say “tend to” because Android 5.0’s elevation stuff also plays a role, and the relationship between them is ill-defined)

So, to have a floating action button (FAB) float over a ScrollView in a RelativeLayout, make sure that the ScrollView is defined first in the XML, with the FAB after it. This will not affect the X/Y rules, but it should have the FAB appear over the ScrollView on the Z axis.

Another possibility, if you are only supporting Android 5.0+, would be to use android:elevation itself to raise the FAB.

Categories
discuss

Associate Data With HTML Element (without jQuery)

I need to associate some data with an HTML Element, hopefully avoiding any memory leaks. Obviously a simple solution would be to throw some kind of identifier on the element, and then create a dictionary mapping that identifier to the data I need. However, this is in a javascript library that users will add to their page, thus I don’t have control over when elements are added or removed.

What I’m looking to do is associate data with an HTML element while it’s on the page, while allowing for that data to be GC’d when/if the element is removed. Is there a way to do this that doesn’t involve writing my own periodic GC to clean up orphaned data? Is it safe to add properties to HTML elements?

Answer

Attribute approach

You can store data in elements using custom data-* attributes.

This has the limitation that you can only store strings, but you can use JSON to store plain objects or arrays (not by reference).

To avoid conflicts with other code, it would be a good idea to include the name of your library in the attribute.

They can be set directly in the HTML:

<div data-mylibrary-foo="bar"></div>

And they can be read or written with JavaScript:

element.getAttribute('data-mylibrary-foo');        // read  (old way)
element.setAttribute('data-mylibrary-foo', 'bar'); // write (old way)
element.dataset.mylibraryFoo;                      // read  (new way)
element.dataset.mylibraryFoo = 'bar';              // write (new way)

They can also be read by CSS, using some attribute selector.

Property approach

This is much more flexible than the attribute approach, allowing to store arbitrary data in your element.

To avoid conflicts with other code, better wrap all properties in an object with the name of your library:

element.mylibrary = {};
element.mylibrary.foo = 'bar';

The problem is that a future HTML standard could define mylibrary as a native property, so there could be problems.

Symbol approach

ECMAScript 6 introduces symbols, which can be used as properties. The advantage is that each symbol has an unique identity, so you don’t need to worry about some other library or a future standard using the same properties as your code.

var foo = Symbol("foo");
element[foo] = 'bar';

WeakMap approach

ECMAScript 6 introduces WeakMaps, which allow you to associate data with objects in a way that, if the objects are no longer referenced anywhere else, they will be garbage collected.

Like the property approach, they allow you to store arbitrary data.

Since the data is not stored in the elements themselves, there is no risk of conflicts.

var allData = new WeakMap();
var data1 = allData.get(element1) || {}; // read
data1.foo = "bar";
allData.set(element1, data1);            // write
Categories
discuss

Can’t disable android.widget.Button

I have a button inside Android widget, declared like this:

<Button android:id="@+id/newWorkBtnWidget"
android:layout_width="fill_parent" 
android:layout_height="wrap_content"
android:text="@string/ts_on_repair"/> 

Every time I try to enable or disable this button via RemoteViews I get an error android.widget.RemoteViews$ActionException: view: android.widget.Button can't use method with RemoteViews: setEnabled(boolean)

My code:

RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
                        R.layout.widget);

remoteViews.setInt(R.id.newWorkBtnWidget, "setBackgroundResource",
    R.drawable.green_button);
remoteViews.setBoolean(R.id.newWorkBtnWidget, 
   "setEnabled", false);

How to fix it?

Answer

I had same problem recently, and I found that remoteView.setBoolean() with methodname ‘setEnabled’ doesn’t work either.

use

remoteViews.setBoolean(R.id.textView_life_id, "setEnabled", true);

will get error like this

W/AppWidgetHostView: updateAppWidget couldn't find any view, using error view
                                              android.widget.RemoteViews$ActionException: view: android.widget.TextView can't use method with RemoteViews: setEnabled(boolean)
                                                  at android.widget.RemoteViews$ReflectionAction.apply(RemoteViews.java:1134)
                                                  at android.widget.RemoteViews.performApply(RemoteViews.java:2304)
                                                  at android.widget.RemoteViews.apply(RemoteViews.java:2263)
                                                  at android.appwidget.AppWidgetHostView.updateAppWidget(AppWidgetHostView.java:402)
                                                  at com.android.launcher2.LauncherAppWidgetHostView.updateAppWidget(LauncherAppWidgetHostView.java:54)
                                                  at android.appwidget.AppWidgetHost.updateAppWidgetView(AppWidgetHost.java:434)
                                                  at android.appwidget.AppWidgetHost$UpdateHandler.handleMessage(AppWidgetHost.java:102)
                                                  at android.os.Handler.dispatchMessage(Handler.java:99)
                                                  at android.os.Looper.loop(Looper.java:138)
                                                  at android.app.ActivityThread.main(ActivityThread.java:5089)
                                                  at java.lang.reflect.Method.invokeNative(Native Method)
                                                  at java.lang.reflect.Method.invoke(Method.java:511)
                                                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
                                                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
                                                  at dalvik.system.NativeStart.main(Native Method)

finally I found the answer is that only method in SDK with @android.view.RemotableViewMethod can be used in remoteview.

like

@android.view.RemotableViewMethod
public final void setText(CharSequence text) {
     setText(text, mBufferType);
}

then you can use views.setCharSequence(R.id.textView, “setText”, “Test”);

Categories
discuss

Why do projects like angular have their own version of common functions?

I am trying to understand or refresh my logic on this better – for example in angular it has the angular.forEach().

I thought it was because the code in a controller(or module in general) – didn’t have access to the browser api (functions and objects, etc) – and for that matter the forEach function of the browser.

But just tested it out as I was trying to understand it better/prove rationale – and both of these console.log() expressions worked.

angular.module('myApp', [])
  .controller('JCtrl', ['$scope', function($scope) {
    $scope.test = 'scope and binding works'; 

    [0, 1, 4].forEach(function(value) {
      console.log(value);
    });
    console.log([].forEach);
}]);

here is the plnkr

Answer

Directly from github source code of Angular.js that you can find here

/**
* @ngdoc function
* @name angular.forEach
* @module ng
* @kind function
*
* @description
* Invokes the `iterator` function once for each item in `obj` collection, which can be either an
* object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
* is the value of an object property or an array element, `key` is the object property key or
* array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
*
* It is worth noting that `.forEach` does not iterate over inherited properties because it filters
* using the `hasOwnProperty` method.
*
* Unlike ES262's
* [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
* Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
* return the value provided.
*
  ```js
    var values = {name: 'misko', gender: 'male'};
    var log = [];
    angular.forEach(values, function(value, key) {
      this.push(key + ': ' + value);
    }, log);
    expect(log).toEqual(['name: misko', 'gender: male']);
  ```
*
* @param {Object|Array} obj Object to iterate over.
* @param {Function} iterator Iterator function.
* @param {Object=} context Object to become context (`this`) for the iterator       function.
* @returns {Object|Array} Reference to `obj`.
*/

So, as you can see

Unlike ES262’s Array.prototype.forEach, providing ‘undefined’ or ‘null’ values for obj will not throw a TypeError, but rather just return the value provided.

These kind of things are done because when you design your own system, there is a high possibility that you will need something more other than the original.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..