2016-02-05 54 views
2

Скажем, у меня есть карта, которая выглядит, как этотScala фильтр множества

val map = Map("Shoes" -> 1, "heels" -> 2, "sneakers" -> 3, "dress" -> 4, "jeans" -> 5, "boyfriend jeans" -> 6) 

А также у меня есть набор или коллекцию, которая выглядит следующим образом:

val set = Array(Array("Shoes", "heels", "sneakers"), Array("dress", "maxi dress"), Array("jeans", "boyfriend jeans", "destroyed jeans")) 

Я хотел бы выполнить фильтр на моей карте, чтобы сохранить только один элемент в каждом из моих наборов. Ожидаемый результат должен быть что-то вроде этого:

map = Map("Shoes" -> 1, "dress" -> 4 ,"jeans" -> 5) 

Цель делает это так, что если у меня есть несколько наборов, которые указывают на различные категории одежды, мой выход карты не «повторить» себя на технически одни и те же объекты, ,

Любая помощь приветствуется, спасибо!

+0

Как вы выбираете, какой набор элементов или карта ключевых побед над другими, когда они оба перекрытия? – Daenyth

+0

Не имеет значения. Может просто выбрать первый элемент в наборе. –

+0

Не могли бы вы немного расширить свои примеры - не можете понять, что вы пытаетесь сделать ... tks – wwkudu

ответ

3

Итак, сначала избавитесь от путаницы, что ваши наборы являются массивами. Для остальной части примера я буду использовать это определение вместо:

val arrays = Array(Array("Shoes", "heels", "sneakers"), Array("dress", "maxi dress"), Array("jeans", "boyfriend jeans", "destroyed jeans")) 

Таким образом, в некотором смысле у вас есть массив массивов эквивалентных объектов и хотите удалить все, кроме одного из них?

Ну, сначала вы должны найти, какой из элементов в массиве фактически используется в качестве ключей в мегацентре. Поэтому мы просто отфильтровываем все элементы, которые не используются в качестве ключей:

array.filter(map.keySet) 

Теперь мы должны выбрать один элемент. Как вы сказали, мы просто взять первый:

array.filter(map.keySet).head 

в качестве «наборов» на самом деле являются массивы, это действительно первый элемент в массиве, который также используется в качестве ключа. Если бы вы действительно использовали наборы, этот код все равно работал бы, поскольку наборы фактически имеют «первый элемент». Это просто специфическая реализация, и она может даже не быть детерминированной по сравнению с различными исполнениями одной и той же программы. По крайней мере, для неизменяемых наборов он должен, однако, быть детерминированным по нескольким вызовам в голову, т. Е. Вы всегда должны получать один и тот же элемент.

Вместо первого элемента мы на самом деле заинтересованы во всех остальных элементов, так как мы хотим, чтобы удалить их с карты:

array.filter(map.keySet).tail 

Теперь мы просто должны удалить те из карты:

map -- array.filter(map.keySet).tail 

И сделать это для всех массивов:

map -- arrays.flatMap(_.filter(map.keySet).tail) 

Это прекрасно работает до тех пор, как массивы реже. Если это не так, мы не можем взять исходную карту для фильтрации массива на каждом шаге. Вместо этого мы должны использовать один массив для вычисления новой карты, а затем выполнить следующий запуск с результатом из последнего и так далее. К счастью, мы не должны делать:

arrays.foldLeft(map){(m,a) => m -- a.filter(m.keySet).tail} 

Примечание: Множества и функции от элементов к Boolean, это, почему это решение работает.

+0

Элегантный! Это лучший способ решить эту конкретную проблему, хотя я оставлю свой ответ, поскольку он более широко применим к связанным проблемам. –

+0

Или 'map.filterKeys', берущие те, которые являются главой их группы. –

+1

У меня было три причины не выбирать map.filterKeys, прежде всего, в наборах вопросов на самом деле массивы, фильтрация чего-то относительно массива была бы медленной. Тогда я бы потерял порядок массива, потому что результат filterKeys имел бы порядок карты. Сохранение порядка позволяет мне на самом деле выбрать четко определенный первый элемент. И, наконец, я предположил, что набор будет небольшим, но карта, возможно, большая, а filterKeys - линейная операция. Таким образом, преобразование линейно по размеру множества и эффективно не зависит от размера карты. – dth

2

Основная идея заключается в использовании groupBy.Что-то вроде

map.groupBy{ case (k,v) => g(k) }. 
    map{ case (_, kvs) => kvs.head } 

Это общий способ группы подобных вещей (с помощью функции g). Теперь вопрос только в том, как сделать g, что вам нужно. Один из способов:

val g = set.zipWithIndex. 
    flatMap{ case (a, i) => a.map(x => x -> i) }. 
    toMap 

который наклеивает каждый набор на число, а затем формирует карту, чтобы вы могли ее просмотреть. Карты имеют функцию приложения, поэтому вы можете использовать ее, как указано выше.

+0

Я согласен с вашим выбором groupBy, и ваш пример * так * гораздо более лаконичен, чем запутанный ответ, который я писал. – pndc

+0

Я действительно не получаю этот ответ. Во-первых, в определении 'g' есть некоторая ошибка:' a' - это элемент набора (т. Е. Строка), а ваш - над ним. Когда вы просматриваете его, вы создаете карту от символов до индексов. Также в первой части 'kvs.head' уже есть кортеж, почему вы берете его appart и помещаете обратно? – dth

+0

@dth - я исправил конструкцию/деконструкцию кортежа, спасибо! (Забыл прочитать, что я закончил после редактирования). Но я думаю, что имя переменной 'set' вводит вас в заблуждение - какой тип это в самом деле в исходном вопросе? –

2

Этот код решает эту проблему:

var newMap = map 

set.foreach { list => 
    var remove = false 
    list.foreach { _key => 
    if (remove) { 
     newMap -= _key 
    } 
    if (newMap.contains(_key)) { 
     remove = true 
    } 

    } 
} 

Я совершенно новое в Scala. Я воспринял это как свой первый пример Scala , пожалуйста, любые подсказки от гуру Scala приветствуются.

+0

Ну, это в основном императивный способ сделать это. Если вы программируете так, вы можете даже рассмотреть возможность использования измененной карты, поскольку она может быть быстрой. (Не в этом случае, потому что вы касаетесь только нескольких элементов, поэтому преобразование в и с изменчивой карты было бы намного дороже) – dth

+0

Проблема в том, что это сложнее, чем функциональная версия. Здесь вы должны понять, что делает алгоритм, тогда как функциональную версию можно понять шаг за шагом. – dth

+0

Во-первых, спасибо за понимание, во-вторых, мне нравится, как вы его решаете, и я уверен, что сделаю это так же, если я где-нибудь буду знать эти функции. Посмотрите вперед, чтобы узнать немного о Scala. –

1

Несколько упрощенная версия

set.flatMap(_.find(map.contains).map(y => y -> map(y))) 
+0

Ницца. Просто добавьте '.toMap', и все готово. – jwvh

Смежные вопросы