All Downloads are FREE. Search and download functionalities are using the official Maven repository.

no.ssb.jsonstat.v1.Dataset Maven / Gradle / Ivy

The newest version!
package no.ssb.jsonstat.v1;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import no.ssb.jsonstat.v1.util.IntCartesianProduct;

import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class Dataset {
    private final String id;
    private final Optional label;
    private final List values;
    private final Optional updated;
    private final int[] size;
    private final Map dimensions = new LinkedHashMap<>();
    private final Set requiredDimensions;

    public Dataset(String id, Optional label, List values, Optional updated, List dimensions) {
        this(id, label, values, updated, toDimMap(dimensions));
    }

    public Dataset(String id, Optional label, List values, Optional updated, Map dimensions) {
        this.id = id;
        this.label = label;
        this.values = values;
        this.updated = updated;
        this.dimensions.putAll(dimensions);
        this.size = toSizes(dimensions);
        this.requiredDimensions = buildRequiredDimensionIds();
    }

    public String getId() {
        return id;
    }


    public Optional getLabel() {
        return label;
    }

    public List> getRows(Dimension rowDimension) {
        IntCartesianProduct product = this.asCartasianProduct();
        List asList = product.asList();

        int groupingIndex = rowDimension.getIndex();
        int lastIndex = 0;

        List> rows = new ArrayList<>();
        for (int i = 0; i < rowDimension.getSize(); i++) {
            List row = new ArrayList<>();
            for (int j = lastIndex; j < asList.size(); j++) {
                int[] coord = asList.get(j);
                int cv = coord[groupingIndex];
                if (cv == i) {
                    row.add(this.getValue(coord));
                } else if (cv == i + 1) {
                    lastIndex = j;
                    break;
                }
            }
            rows.add(row);
        }
        return rows;
    }

    public List getSlice(Map dimensionCategories) {
        int[] dimensionIndices = getDimensionIndices(dimensionCategories);
        Set missing = validateRequiredDimensions(dimensionCategories.keySet());
        List result = new ArrayList<>();
        if (missing.isEmpty()) {
            result.add(getValue(dimensionIndices));
        }
        else if (missing.size() == 1) {
            int missingIndex = indexOf(dimensionIndices, -1);
            String id = missing.iterator().next();
            Dimension dim = dimensions.get(id);

            for (int c = 0; c < dim.getSize(); c++) {
                int[] clone = dimensionIndices.clone();
                clone[missingIndex] = c;
                result.add(getValue(clone));
            }
        }
        else {
            throw new IllegalArgumentException("More than one dimension was missing: " + missing);
        }

        return result;
    }

    public Data getValue(Map dimensionCategories) {
        Set missing = validateRequiredDimensions(dimensionCategories.keySet());
        if (!missing.isEmpty()) {
            throw new IllegalArgumentException(
                    String.format(
                            "To get a single value, all dimensions must be present. %s is missing",
                            missing
                    )
            );
        }
        return getValue(getDimensionIndices(dimensionCategories));
    }

    public Data getValue(int[] dimensions) {
        return getValue(indexInCube(dimensions));
    }

    public Data getValue(int index) {
        return values.get(index);
    }

    public IntCartesianProduct asCartasianProduct() {
        return new IntCartesianProduct(size.clone());
    }

    public List getValues() {
        return values;
    }

    public Optional getUpdated() {
        return updated;
    }

    public Optional getDimension(String id) {
        return Optional.ofNullable(dimensions.get(id));
    }

    public List getDimensions() {
        return ImmutableList.copyOf(dimensions.values());
    }

    public int size() {
        return values.size();
    }

    private int indexInCube(int[] dimension) {
        int mult = 1;
        int num = 0;
        for (int i = 0; i < size.length; i++) {
            mult *= (i > 0) ? size[size.length-i] : 1;
            num += mult * dimension[size.length-i-1];
        }
        return num;
    }

    private int indexOf(int[] arr, int value) {
        for (int i = 0; i < arr.length; i++) {
            int v = arr[i];
            if (v == value) {
                return i;
            }
        }
        return -1;
    }

    private int[] getDimensionIndices(Map dimensionCategories) {
        int[] indices = new int[dimensions.size()];
        int index = 0;
        for (Map.Entry entry : dimensions.entrySet()) {
            Dimension dimension = entry.getValue();
            if (dimension.isConstant()) {
                indices[index] = 0;
            } else {
                String catID = dimensionCategories.get(entry.getKey());
                if (catID != null) {
                    indices[index] = dimension.getCategoryIndex(catID);
                }
                else {
                    indices[index] = -1;
                }
            }
            index++;
        }
        return indices;
    }

    private Set validateRequiredDimensions(Set ids) {
        return Sets.difference(requiredDimensions, ids);
    }

    private Set buildRequiredDimensionIds() {

        // TODO Document better. As far as I can tell for now, this basically filters
        // TODO out the non required dimension ids.
        ImmutableList dimensions = ImmutableList.copyOf(this.dimensions.values());
        return dimensions.stream().flatMap(dimension -> {
            return dimension.isRequired() ? Stream.of(dimension.getId()) : Stream.of((String) null);
        }).collect(Collectors.toSet());

    }

    private int[] toSizes(Map dimensions) {
        int[] sizes = new int[dimensions.size()];
        int i = 0;
        for (Dimension dimension : dimensions.values()) {
            sizes[i] = dimension.getSize();
            i++;
        }
        return sizes;
    }

    private static Map toDimMap(Iterable dimensions) {
        LinkedHashMap map = new LinkedHashMap<>();
        for (Dimension dimension : dimensions) {
            map.put(dimension.getId(), dimension);
        }
        return map;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy